2015-2016 ptz Moscow 补题记录
懒得写了就随便记一下
A. ABBA
tag
题意
太复杂了,略。
思路
本质上是求秩的大小,高斯消元即可。
复杂度 O(n3) 。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
|
#include<bits/stdc++.h> using namespace std;
using i64=long long;
const int mod=998244353; const int N=2e2+5;
i64 power(i64 a,i64 b=mod-2){ i64 res=1; while(b){ if(b&1) res=res*a%mod; a=a*a%mod; b>>=1; } return res; }
int n,m; struct Matrix{ i64 val[N][N]; int gauss_jordan(){ for(int i=1;i<=n;++i){ int r=i; for(int j=i+1;j<=n;++j) if(abs(val[r][i])<abs(val[j][i])) r=j; if(r!=i) for(int j=1;j<=m;++j) swap(val[i][j],val[r][j]); if(!val[i][i]) return i-1; for(int j=1;j<=n;++j) if(j!=i){ i64 tmp=val[j][i]*power(val[i][i])%mod; for(int k=i+1;k<=m;++k){ val[j][k]+=(mod-val[i][k]*tmp%mod)%mod; val[j][k]%=mod; } } } for(int i=1;i<=n;i++){ i64 in=power(val[i][i]); for(int j=n+1;j<=m;j++) val[i][j]=val[i][j]*in%mod; } return n; } }mat;
int main(){ ios::sync_with_stdio(0); cin.tie(0);cout.tie(0);
cin>>n>>m;
if(n<=m){ for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>mat.val[i][j]; } else{ swap(n,m); for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) cin>>mat.val[j][i]; }
cout<<mat.gauss_jordan();
return true&&false; }
|
C. Mr. Credo
tag
题意
给定 n 个光源的坐标 x,y 与照射角度范围,保证所有角度加起来小于 2⋅π ,找一个半径为 1 的圆使得不被照到。
其中 1≤n≤2⋅105,−50≤xi,yi≤50 。
思路
构造的圆心坐标范围可以达到 [−109,109] ,光源的坐标范围为 [−50,50] 相差很大,可以认为所有光源均在原点,找一个没有被照射的角度将圆取在尽可能远的位置即可。
复杂度 O(n) 。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
|
#include<bits/stdc++.h> using namespace std;
using i64=long long;
const long double pi=acos(-1.0);
int main(){ ios::sync_with_stdio(0); cin.tie(0);cout.tie(0);
int n; cin>>n; vector<int> f(1296000,0); for(int i=1,x,y,l,r;i<=n;i++){ cin>>x>>y>>r>>l; r+=l-1; r%=1296000; if(l<=r){ f[l]++; if(r+1<1296000) f[r+1]--; } else{ f[l]++; f[0]++; if(r+1<1296000) f[r+1]--; } } for(int i=1;i<1296000;i++) f[i]+=f[i-1];
vector<array<int,2>> vec; for(int i=0;i<1296000;){ if(f[i]==0){ int j=i; while(j+1<1296000&&f[j+1]==0) j++; vec.push_back({i,j}); i=j+1; } else i++; } if(f[0]==0&&f[1269000-1]==0){ vec[0][0]=vec.back()[0]; vec.pop_back(); }
int len=-1; long double ang=0; for(auto [l,r]:vec){ int newlen=0; if(l<=r) newlen=r-l+1; else{ l-=1296000; newlen=r-l+1; l+=1296000; } if(newlen>len){ len=newlen; if(l<=r) ang=pi*2.0*((1.0*l+r+1)/2.0)/1296000.0; else{ l-=1296000; ang=pi*2.0*((1.0*l+r+1)/2.0)/1296000.0; } } }
cout<<"YES\n"<<(int)(cos(ang)*1e9)<<" "<<(int)(sin(ang)*1e9);
return true&&false; }
|
D. Deep Purple
tag
题意
给定长为 n 的小写字母串 s , q 次询问,每次询问区间 [l,r] 的最长 border 。
其中 1≤n,q≤2⋅105,1≤li,ri≤n 。
思路
考虑单次询问,即求最大的 i 满足 lcs(i,r)>i−l ,对 s 建出 SAM ,记 posx 为 endpos=x 的节点在后缀树上的编号, i 的条件即为存在节点 x 满足:
- x 为 posr 的祖先节点
- x 的子树中存在节点 i 使得 len(x)+l>endposi
求出满足条件的最大的 i ,若 i≥l ,询问答案即为 i−l+1 。
对于单次询问,可以先将后缀树上 posr 到根的路径上每个节点加上权值 len(x)+l ,再从大到小枚举 i ,找到第一个 i 满足: i 到根的路径上存在节点的标记值大于 i 。用 i 更新答案即可。
对于多次询问,可以将询问离线下来处理,从大到小枚举 i ,遇到询问右端点 r 就将 posr 到根这段路径打上权值 len(x)+l ,再查询 i 能更新的询问,更新完成或者不能再被更新后就把对应询问打上的标记删去。感觉这部分将询问一起处理有点类似整体二分的过程。
现在需要能够加 / 删标记与查询标记最大值,可以树剖 + 线段树维护。
对于 posr 到根的这段路径,将它拆成 log 条重链,分两颗线段树 tr0,tr1 来维护, tr0 维护加上的 l 的最大值, tr1 维护 len(x)+l 的最大值,加 / 删标记时对于每条重链只将 l 加 / 删在链尾并在线段树上修改对应节点的值(树上每个节点维护一个 set 记录加 / 删在当前节点的权值,线段树只负责维护一个区间 max )。
查询时对每条重链分为两部分,一部分为 tr1 直接查询的区间的值,另一部分为最深的节点的 len 加上 tr0 查询的整条链的值,因为 len(x) 随着 x 的深度增加而单调增,所以查询结果一定在其中一个中。
复杂度 O(n⋅log2n) 。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
|
#include<bits/stdc++.h> using namespace std;
using i64=long long;
const int N=2e5+5; const int M=26;
struct Seg_Tree{ int l[N<<3]; int r[N<<3]; array<int,2> maxn[N<<3]; void pushup(int x){ maxn[x]=max(maxn[x<<1],maxn[x<<1|1]); } void build(int x,int L,int R){ l[x]=L; r[x]=R; maxn[x]={0,0}; if(L==R) return; int mid=L+R>>1; build(x<<1,L,mid); build(x<<1|1,mid+1,R); return; } void change(int x,int pos,array<int,2> val){ if(l[x]==r[x]){ maxn[x]=val; return; } if(pos<=r[x<<1]) change(x<<1,pos,val); else change(x<<1|1,pos,val); pushup(x); return; } array<int,2> search(int x,int ql,int qr){ if(l[x]>=ql&&r[x]<=qr) return maxn[x]; array<int,2> res={0,0}; if(r[x<<1]>=ql) res=max(res,search(x<<1,ql,qr)); if(l[x<<1|1]<=qr) res=max(res,search(x<<1|1,ql,qr)); return res; } }tr0,tr1; struct SAM{ int tot; int nex[N*2][M]; int len[N*2]; int link[N*2]; int pos[N]; int lst; void clear(){ tot=0; len[0]=lst=0; link[0]=-1; memset(nex[0],0,sizeof(nex[0])); } SAM(){ clear(); } int addnode(){ tot++; len[tot]=0; link[tot]=-1; memset(nex[tot],0,sizeof(nex[tot])); return tot; } void cpy(int x,int y){ for(int i=0;i<M;i++) nex[x][i]=nex[y][i]; len[x]=len[y]; link[x]=link[y]; } void extend(char c,int ind){ int w=c-'a'; int u=addnode(); len[u]=len[lst]+1; int p=lst; while(p!=-1&&!nex[p][w]){ nex[p][w]=u; p=link[p]; } if(p==-1) link[u]=0; else{ int q=nex[p][w]; if(len[p]+1==len[q]) link[u]=q; else{ int clone=addnode(); cpy(clone,q); len[clone]=len[p]+1; while(p!=-1&&nex[p][w]==q){ nex[p][w]=clone; p=link[p]; } link[q]=link[u]=clone; } } lst=u; pos[ind]=lst; } }sam; namespace TCD{ const int N=::N; vector<int> e[N*2]; int rt,dfn0; int sz[N*2],fa[N*2]; int dfn[N*2],son[N*2],top[N*2],tail[N*2]; set<array<int,2>> st[N*2]; void init(int n,int _rt){ rt=_rt; dfn0=0; for(int i=0;i<=n;i++){ sz[i]=1; fa[i]=dfn[i]=top[i]=tail[i]=0; son[i]=-1; e[i].clear(); } } void add(int x,int y){ e[x].push_back(y); } void getson(int x,int f){ fa[x]=f; for(auto v:e[x]){ if(v==f) continue; getson(v,x); sz[x]+=sz[v]; if(son[x]==-1||sz[v]>sz[son[x]]) son[x]=v; } } void dfs(int x,int f,int tp){ dfn[x]=++dfn0; top[x]=tp; tail[tp]=max(tail[tp],dfn[x]); if(son[x]!=-1) dfs(son[x],x,tp); for(auto v:e[x]){ if(v==f||v==son[x]) continue; dfs(v,x,v); } } void divide(){ getson(rt,-1); dfs(rt,-1,rt); tr0.build(1,1,dfn0); tr1.build(1,1,dfn0); } void insert(int x,array<int,2> arr){ while(x!=-1){ st[x].insert(arr); if(st[x].size()){ auto val=(*st[x].rbegin()); tr0.change(1,dfn[x],val); tr1.change(1,dfn[x],{val[0]+sam.len[x],val[1]}); } else{ tr0.change(1,dfn[x],{0,0}); tr1.change(1,dfn[x],{0,0}); } x=fa[top[x]]; } } void erase(int x,array<int,2> arr){ while(x!=-1){ st[x].erase(arr); if(st[x].size()){ auto val=(*st[x].rbegin()); tr0.change(1,dfn[x],val); tr1.change(1,dfn[x],{val[0]+sam.len[x],val[1]}); } else{ tr0.change(1,dfn[x],{0,0}); tr1.change(1,dfn[x],{0,0}); } x=fa[top[x]]; } } array<int,2> search(int x){ array<int,2> res={0,0}; while(x!=-1){ res=max(res,tr1.search(1,dfn[top[x]],dfn[x])); array<int,2> tmp=tr0.search(1,dfn[x],tail[top[x]]); res=max(res,{tmp[0]+sam.len[x],tmp[1]}); x=fa[top[x]]; } return res; } }
int main(){ ios::sync_with_stdio(0); cin.tie(0);cout.tie(0);
int n,q; cin>>n>>q;
string s; cin>>s;
for(int i=1;i<=n;i++) sam.extend(s[i-1],i); TCD::init(sam.tot,0); for(int i=1;i<=sam.tot;i++) TCD::add(sam.link[i],i); TCD::divide();
vector<int> ans(q+5,0); vector<array<int,2>> qry(q+5,{0,0}); vector<vector<int>> add(n+5,vector<int>(0)); for(int i=1,ql,qr;i<=q;i++){ cin>>ql>>qr; qry[i]={ql,qr}; add[qr].push_back(i); }
for(int i=n;i>=1;i--){ while(1){ array<int,2> res=TCD::search(sam.pos[i]); if(res[1]==0) break; if(res[0]>i){ int id=res[1]; if(i>=qry[id][0]) ans[id]=i-qry[id][0]+1; TCD::erase(sam.pos[qry[id][1]],{qry[id][0],id}); } else break; } for(auto id:add[i]){ auto [ql,qr]=qry[id]; TCD::insert(sam.pos[qr],{ql,id}); } }
for(int i=1;i<=q;i++) cout<<ans[i]<<"\n";
return true&&false; }
|
E. Elvis Presley
tag
题意
阅读理解题,略。
思路
略。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
|
#include<bits/stdc++.h> using namespace std;
using i64=long long;
int main(){ ios::sync_with_stdio(0); cin.tie(0);cout.tie(0);
int x,y; cin>>x>>y;
auto get=[&](int x,int y){ while(x){ if(x==y) return 1; x>>=1; } return 0; };
if(get(x,y)||get(y,x)){ cout<<"-1"; return true&&false; }
set<int> st,ans; x>>=1; y>>=1; while(x){ st.insert(x); x>>=1; } while(y){ st.insert(y); y>>=1; } for(auto val:st){ if(st.find(val<<1)==st.end()) ans.insert(val<<1); if(st.find(val<<1|1)==st.end()) ans.insert(val<<1|1); }
for(auto val:ans) cout<<val<<" ";
return true&&false; }
|
F. Frank Sinatra
tag
题意
给定 n 个节点的树,边有边权 x ,每次查询一条链上边权的 mex 。
其中 2≤n,q≤105,0≤x≤109 。
思路
询问离线下来树上莫队 + bitset 维护即可,大于 n 的边权可以直接忽略,不用离散化。
复杂度 O(n⋅n+wn2) 。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
|
#include<bits/stdc++.h> using namespace std;
using i64=long long;
int main(){ ios::sync_with_stdio(0); cin.tie(0);cout.tie(0);
int n,q; cin>>n>>q; int blo=sqrt(n);
vector<int> bl(2*n+5,0); for(int i=1;i<=2*n;i++) bl[i]=(i-1)/blo+1;
vector<vector<array<int,2>>> e(n+5,vector<array<int,2>>(0)); for(int i=1,x,y,w;i<n;i++){ cin>>x>>y>>w; e[x].push_back({y,w}); e[y].push_back({x,w}); }
int dfn0=0; vector<int> st(n+5,0),en(n+5,0),dep(n+5,0),a(n+5,0); vector<int> dfn(n*2+5,0); vector<array<int,18>> f(n+5); auto get_lca=[&](int x,int y){ if(dep[x]<dep[y]) swap(x,y); for(int i=17;i>=0;i--) if(dep[f[x][i]]>=dep[y]) x=f[x][i]; for(int i=17;i>=0;i--) if(f[x][i]!=f[y][i]){ x=f[x][i]; y=f[y][i]; } while(x!=y){ x=f[x][0]; y=f[y][0]; } return x; };
function<void(int,int)> dfs=[&](int x,int fa){ f[x][0]=fa; for(int i=1;i<18;i++) f[x][i]=f[f[x][i-1]][i-1]; dfn[++dfn0]=x; st[x]=dfn0; for(auto [v,w]:e[x]){ if(v==fa) continue; dep[v]=dep[x]+1; a[v]=w; dfs(v,x); } dfn[++dfn0]=x; en[x]=dfn0; };
dfs(1,1);
vector<array<int,3>> qry(0); for(int i=1,x,y,lca;i<=q;i++){ cin>>x>>y; if(st[x]>st[y]) swap(x,y); lca=get_lca(x,y); if(lca==x) qry.push_back({st[x]+1,st[y],i}); else qry.push_back({en[x],st[y],i}); }
sort(qry.begin(),qry.end(),[&](array<int,3> &a,array<int,3> &b){ if(bl[a[0]]!=bl[b[0]]) return bl[a[0]]<bl[b[0]]; else{ if(bl[a[0]]&1) return a[1]<b[1]; else a[1]>b[1]; } });
int l=1,r=0; vector<int> cnt(n+5,0),vis(n+5,0),ans(q+5,0); bitset<100005> bit; bit.set(); auto add=[&](int ind){ if(a[ind]>n) return; cnt[a[ind]]++; if(cnt[a[ind]]==1) bit.flip(a[ind]); };
auto del=[&](int ind){ if(a[ind]>n) return; cnt[a[ind]]--; if(cnt[a[ind]]==0) bit.flip(a[ind]); };
for(auto [ql,qr,id]:qry){ while(l<ql){ vis[dfn[l]]^=1; if(vis[dfn[l]]) add(dfn[l]); else del(dfn[l]); l++; } while(l>ql){ l--; vis[dfn[l]]^=1; if(vis[dfn[l]]) add(dfn[l]); else del(dfn[l]); } while(r<qr){ r++; vis[dfn[r]]^=1; if(vis[dfn[r]]) add(dfn[r]); else del(dfn[r]); } while(r>qr){ vis[dfn[r]]^=1; if(vis[dfn[r]]) add(dfn[r]); else del(dfn[r]); r--; } ans[id]=bit._Find_first(); }
for(int i=1;i<=q;i++) cout<<ans[i]<<"\n";
return true&&false; }
|
G. Green Day
tag
题意
构造一张图,边总共有k种颜色,要求每种颜色的边均构成一棵生成树且任意两点在任意两棵生成树上的路径的交集只含有路径端点。
其中 1≤k≤100 。
思路
选定点数 n=2⋅k ,结果为一个完全图。
对于 i∈[1,k] ,使 i 向 i+j(i+1≤j≤i+k) 连边, i+k 向 i+k+j(1≤j≤n−1−k) 连边即可。
复杂度 O(k) 。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
#include<bits/stdc++.h> using namespace std;
using i64=long long;
int main(){ ios::sync_with_stdio(0); cin.tie(0);cout.tie(0);
int k,n; cin>>k;
n=k*2; cout<<n<<"\n"; for(int i=1;i<=k;i++){ for(int j=i+1;j<=i+k;j++) cout<<i<<" "<<j<<"\n"; for(int j=1;j<=n-1-k;j++) cout<<i+k<<" "<<(i+k+j-1)%n+1<<"\n"; }
return true&&false; }
|
H. Hans Zimmer
tag
题意
给定 w∗h 的矩形,随机横着或者竖着切 n 次,求最后面积最小的矩形的期望面积。
其中 1≤w,h≤103,1≤n≤106 。
思路
结论:
- 长度为 1 的线段切成 n 段,其中最小段的期望长度为 n21
枚举横着切了 i 刀,结果为:
- i=0∑n2n⋅(i+1)2⋅(n−i+1)2(ni)
但交上去之后发现精度寄了, n 比较大的时候算 2n 直接算裂了。
考虑取对数后再 exp 回去即可。
复杂度 O(n) 。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
#include<bits/stdc++.h> using namespace std;
using i64=long long;
int main(){ ios::sync_with_stdio(0); cin.tie(0);cout.tie(0);
int n; double w,h; cin>>w>>h>>n;
double ans=0,binom=0; for(int i=0;i<=n;i++){ ans+=exp(binom-n*log(2)-2*log(i+1)-2*log(n-i+1)); binom+=log(n-i)-log(i+1); }
cout<<fixed<<setprecision(100)<<ans*w*h;
return true&&false; }
|
I. Ivan Dorn
tag
题意
给定长为 n 的序列 a ,定义一个区间 [l,r] 为 NB 区间当且仅当 al=ar,max[l,r]=al 。
q 次询问,每次询问 [l,r] 内最长的 NB 区间的长度。
其中 1≤n,q≤5⋅105,1≤l,r≤n 。
思路
对于一个询问 [l,r] ,找出 [l,r] 的区间 max 最左边和最右边的位置 posl,posr ,则 [l,r] 被 posl,posr 分割为 2/3 部分,显然中间部分是没有用的,剩下来只要求出左边部分最长能往右延申的长度 lenl 和右边部分最长能往左延申的长度 lenr ,再和 posr−posl 取 max 即可,由于左右部分被区间 max 分隔开,延伸长度一定不会跨过询问区间外,直接取 max 是正确的。
每个位置的延申长度可以单调栈预处理,区间 max 套线段树即可。
复杂度 O(n⋅logn) 。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
|
#include<bits/stdc++.h> using namespace std;
using i64=long long;
const int N=5e5+5;
vector<int> a,val0,val1;
struct tree{ int l[N<<2]; int r[N<<2]; int maxn0[N<<2]; int maxn1[N<<2]; int maxn2[N<<2]; int posl[N<<2]; int posr[N<<2]; void pushup(int x){ maxn0[x]=max(maxn0[x<<1],maxn0[x<<1|1]); maxn1[x]=max(maxn1[x<<1],maxn1[x<<1|1]); maxn2[x]=max(maxn2[x<<1],maxn2[x<<1|1]); if(maxn2[x]==maxn2[x<<1]) posl[x]=posl[x<<1]; else posl[x]=posl[x<<1|1]; if(maxn2[x]==maxn2[x<<1|1]) posr[x]=posr[x<<1|1]; else posr[x]=posr[x<<1]; } void build(int x,int L,int R){ l[x]=L; r[x]=R; if(L==R){ maxn0[x]=val0[L]; maxn1[x]=val1[L]; maxn2[x]=a[L]; posl[x]=posr[x]=L; return; } int mid=L+R>>1; build(x<<1,L,mid); build(x<<1|1,mid+1,R); pushup(x); return; } array<int,3> merge(array<int,3> a,array<int,3> b){ array<int,3> c; c[0]=max(a[0],b[0]); if(c[0]==a[0]) c[1]=a[1]; else c[1]=b[1]; if(c[0]==b[0]) c[2]=b[2]; else c[2]=a[2]; return c; } array<int,3> searchmaxn(int x,int ql,int qr){ if(l[x]>=ql&&r[x]<=qr) return {maxn2[x],posl[x],posr[x]}; if(r[x<<1]>=ql&&l[x<<1|1]<=qr) return merge(searchmaxn(x<<1,ql,qr),searchmaxn(x<<1|1,ql,qr)); else if(r[x<<1]>=ql) return searchmaxn(x<<1,ql,qr); else return searchmaxn(x<<1|1,ql,qr); } int search0(int x,int ql,int qr){ if(l[x]>=ql&&r[x]<=qr) return maxn0[x]; if(r[x<<1]>=ql&&l[x<<1|1]<=qr) return max(search0(x<<1,ql,qr),search0(x<<1|1,ql,qr)); else if(r[x<<1]>=ql) return search0(x<<1,ql,qr); else return search0(x<<1|1,ql,qr); } int search1(int x,int ql,int qr){ if(l[x]>=ql&&r[x]<=qr) return maxn1[x]; if(r[x<<1]>=ql&&l[x<<1|1]<=qr) return max(search1(x<<1,ql,qr),search1(x<<1|1,ql,qr)); else if(r[x<<1]>=ql) return search1(x<<1,ql,qr); else return search1(x<<1|1,ql,qr); } }tr;
int main(){ ios::sync_with_stdio(0); cin.tie(0);cout.tie(0);
int n,q; cin>>n>>q;
a.resize(n+5,0); for(int i=1;i<=n;i++) cin>>a[i]; map<int,vector<int>> pos; for(int i=1;i<=n;i++) pos[a[i]].push_back(i);
vector<int> st; val0.resize(n+5,0); val1.resize(n+5,0); for(int i=1;i<=n;i++){ while(!st.empty()&&a[st.back()]<=a[i]) st.pop_back(); if(st.empty()) val0[i]=i-pos[a[i]].front(); else val0[i]=i-(*upper_bound(pos[a[i]].begin(),pos[a[i]].end(),st.back())); st.push_back(i); } st.clear(); for(int i=n;i>=1;i--){ while(!st.empty()&&a[st.back()]<=a[i]) st.pop_back(); if(st.empty()) val1[i]=pos[a[i]].back()-i; else val1[i]=(*(upper_bound(pos[a[i]].begin(),pos[a[i]].end(),st.back())-1))-i; st.push_back(i); }
tr.build(1,1,n);
while(q--){ int ql,qr; cin>>ql>>qr;
int ans=0; array<int,3> res=tr.searchmaxn(1,ql,qr); ans=max(ans,res[2]-res[1]); if(ql<=res[1]-1) ans=max(ans,tr.search1(1,ql,res[1]-1)); if(res[2]+1<=qr) ans=max(ans,tr.search0(1,res[2]+1,qr));
cout<<ans<<"\n"; }
return true&&false; }
|
J. Jimi Hendrix
tag
题意
给定 n 个节点的树,边有边权为小写字母,问树上是否存在一条链使得链组成的小写字母串包含给定串 s 作为一个子序列。
其中 1≤n≤5⋅105,1≤∣s∣≤n−1 。
思路
树 DP 即可。记一下每个节点的子树中正 / 倒着匹配 s 能匹配到的最长长度和起 / 终点。
复杂度 O(n) 。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
|
#include<bits/stdc++.h> using namespace std;
using i64=long long; using pic=pair<int,char>;
int main(){ ios::sync_with_stdio(0); cin.tie(0);cout.tie(0);
int n,m; cin>>n>>m;
vector<vector<pic>> e(n+5,vector<pic>(0)); for(int i=1,x,y;i<n;i++){ char c; cin>>x>>y>>c; e[x].push_back({y,c}); e[y].push_back({x,c}); }
string s; cin>>s;
array<int,2> ans={-1,-1}; vector<array<int,2>> pre(n+5,{0,0}),suf(n+5,{0,0}); function<void(int,int)> dfs=[&](int x,int fa){ array<int,2> maxpre={0,0},maxsuf={0,0}; for(auto [v,c]:e[x]){ if(v==fa) continue; dfs(v,x); maxpre={0,0}; maxsuf={0,0}; if(pre[v][0]<m&&s[pre[v][0]]==c){ maxpre={pre[v][0]+1,pre[v][1]}; if(maxpre[0]==1) maxpre[1]=v; } else maxpre=pre[v]; if(m-suf[v][0]-1>=0&&s[m-suf[v][0]-1]==c){ maxsuf={suf[v][0]+1,suf[v][1]}; if(maxsuf[0]==1) maxsuf[1]=v; } else maxsuf=suf[v]; if(pre[x][0]+maxsuf[0]>=m) ans={pre[x][1],maxsuf[1]}; if(maxpre[0]+suf[x][0]>=m) ans={maxpre[1],suf[x][1]}; pre[x]=max(pre[x],maxpre); suf[x]=max(suf[x],maxsuf); } if(pre[x][0]>=m) ans={pre[x][1],x}; if(suf[x][0]>=m) ans={x,suf[x][1]}; };
dfs(1,1);
cout<<ans[0]<<" "<<ans[1];
return true&&false; }
|
K. Korn
tag
题意
给定 n 和点 m 条边的无向连通图,求 NB 点的个数。
一个点为 NB 点当且仅当从这个点开始走的所有回路均为原图的一条欧拉回路。
其中 3≤n≤2⋅105,n−1≤m≤5⋅105 。
思路
首先将存在奇数度数点的情况特判掉。
剩下来的情况中一个点为 NB 点当且仅当所有环均经过它,跑出原图的 dfs 树判一下即可。
复杂度 O(n) 。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
|
#include<bits/stdc++.h> using namespace std;
using i64=long long;
int main(){ ios::sync_with_stdio(0); cin.tie(0);cout.tie(0);
int n,m; cin>>n>>m;
vector<vector<int>> e(n+5,vector<int>(0)); for(int i=1,x,y;i<=m;i++){ cin>>x>>y; e[x].push_back(y); e[y].push_back(x); }
for(int i=1;i<=n;i++) if(e[i].size()&1){ cout<<"0"; return true&&false; }
int dfn0=0; vector<int> dfn(n+5,0),low(n+5,0); vector<int> ans; function<void(int,int)> dfs=[&](int x,int fa){ int cnt=0; dfn[x]=low[x]=++dfn0; for(auto v:e[x]){ if(v==fa) continue; if(dfn[v]){ cnt++; low[x]=min(low[x],dfn[v]); } else{ dfs(v,x); cnt+=(low[v]<dfn[x]); low[x]=min(low[x],low[v]); } } if(m-(n-1)==cnt) ans.push_back(x); };
dfs(1,0); sort(ans.begin(),ans.end());
cout<<ans.size()<<"\n"; for(auto x:ans) cout<<x<<" ";
return true&&false; }
|