题目见LOJ
T1
先建SAM转成两两LCA的len的最大值
考场搞了一个根号带俩log的算法,还写挂了。。。
正解据zrq说是个套路:显然我们对于一些相同的后缀只考虑右侧的更优。于是从左往右扫,维护每个点到根路径上它右侧right集合中的最大值,每次直接爬parent树更新,询问就在右端点询问左端点右侧的最大值。复杂度如何保证?发现这个东西和LCT的Access是一个道理,于是就能保证了
同理也可以快速做区间本质不同的子串数目,转一下i207M的:
“每次新加一个后缀,然后把从该点到根的链拿出来,维护一个线段树表示答案,把原来right集合的最大点的权值-=len,把当前r的权值+=len”
所以留个问题:询问parent树上一些点两两LCA中len的最大值,将每个点到根的路径+1,取所有被加了两次以上的点求全局max,对吗?
1 #include<cstdio> 2 #include<cctype> 3 #include<vector> 4 #include<cstring> 5 #include<algorithm> 6 #define vpii vector<pair<int,int> > 7 #define vpit vector<pair<int,int> > ::iterator 8 using namespace std; 9 const int N=200005; 10 int n,m,t1,t2,ans[N]; 11 char str[N]; vpii qry[N]; 12 void Maxi(int &x,int y){if(x<y) x=y;} 13 14 int bit[N]; 15 void Change(int pos,int tsk) 16 { 17 pos=n-pos+1; 18 while(pos<=n) 19 Maxi(bit[pos],tsk),pos+=pos&-pos; 20 } 21 int Query(int pos) 22 { 23 int ret=0; 24 pos=n-pos+1; 25 while(pos) 26 Maxi(ret,bit[pos]),pos-=pos&-pos; 27 return ret; 28 } 29 30 int trs[N][2],fth[N],len[N],pts[N],tot,lst; 31 int Insert(int ch) 32 { 33 int nde=lst,newn=++tot; 34 lst=newn,len[newn]=len[nde]+1; 35 while(nde&&!trs[nde][ch]) 36 trs[nde][ch]=newn,nde=fth[nde]; 37 if(!nde) fth[newn]=1; 38 else 39 { 40 int tran=trs[nde][ch]; 41 if(len[tran]==len[nde]+1) 42 fth[newn]=tran; 43 else 44 { 45 int rnde=++tot; len[rnde]=len[nde]+1; 46 trs[rnde][0]=trs[tran][0],trs[rnde][1]=trs[tran][1]; 47 fth[rnde]=fth[tran],fth[tran]=fth[newn]=rnde; 48 while(nde&&trs[nde][ch]==tran) 49 trs[nde][ch]=rnde,nde=fth[nde]; 50 } 51 } 52 return newn; 53 } 54 55 int anc[N],son[N][2],val[N],laz[N],stk[N],top; 56 void Release(int nde) 57 { 58 if(laz[nde]) 59 { 60 int ls=son[nde][0],rs=son[nde][1],lz=laz[nde]; 61 if(ls) val[ls]=laz[ls]=lz; 62 if(rs) val[rs]=laz[rs]=lz; lz=0; 63 } 64 } 65 bool Nottop(int nde) 66 { 67 int fa=anc[nde]; 68 return son[fa][0]==nde||son[fa][1]==nde; 69 } 70 void Rotate(int nde) 71 { 72 int fa=anc[nde],gr=anc[fa],isl=nde==son[fa][0]; 73 if(Nottop(fa)) son[gr][fa==son[gr][1]]=nde; 74 anc[nde]=gr,anc[fa]=nde,anc[son[nde][isl]]=fa; 75 son[fa][isl^1]=son[nde][isl],son[nde][isl]=fa; 76 } 77 void Splay(int nde) 78 { 79 stk[top=1]=nde; 80 for(int i=nde;Nottop(i);i=anc[i]) 81 stk[++top]=anc[i]; 82 while(top) Release(stk[top--]); 83 while(Nottop(nde)) 84 { 85 int fa=anc[nde],gr=anc[fa]; 86 if(Nottop(fa)) 87 Rotate(((son[fa][0]==nde)==(son[gr][0]==fa))?fa:nde); 88 Rotate(nde); 89 } 90 } 91 void Access(int nde,int tsk) 92 { 93 int lst=0,mem=nde; 94 while(nde) 95 { 96 Splay(nde),Change(val[nde],len[nde]); 97 son[nde][1]=lst,lst=nde,nde=anc[nde]; 98 } 99 val[lst]=laz[lst]=tsk,Splay(mem); 100 } 101 102 int main() 103 { 104 scanf("%d%d%s",&n,&m,str+1),lst=tot=1; 105 for(int i=1;i<=n;i++) pts[i]=Insert(str[i]-'0'); 106 for(int i=1;i<=tot;i++) anc[i]=fth[i]; 107 for(int i=1;i<=m;i++) 108 { 109 scanf("%d%d",&t1,&t2); 110 qry[t2].push_back(make_pair(i,t1)); 111 } 112 for(int i=1;i<=n;i++) 113 { 114 Access(pts[i],i); 115 for(vpit it=qry[i].begin();it!=qry[i].end();it++) 116 ans[it->first]=Query(it->second); 117 } 118 for(int i=1;i<=m;i++) 119 printf("%d ",ans[i]); 120 return 0; 121 }
T2
显然题目要我们把每个点改成重心,利用重心的性质:最大的子树不超过总大小的$frac{1}{2}$
然后DP失败
根据上面的性质显然每个点(除了已经是重心的以外)最多有一个子树破坏了这个性质,而且这个重心肯定也在这个子树里,我们要做的就是从这个子树里砍掉一些使得它的大小不超过总大小的$frac{1}{2}$,砍掉的直接接在新的重心上就行(当然你不能把超过总大小的$frac{1}{2}$的接过来=。=)
发现好像不需要DP,可以建立权值线段树后二分来贪心。。。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int N=1000005; 6 int n,t1,t2,cor,cnt,tot,maxs; 7 int p[N],noww[2*N],goal[2*N]; 8 int siz[N],anc[N],val[4*N],num[4*N]; 9 void Maxi(int &x,int y){if(x<y) x=y;} 10 void Link(int f,int t) 11 { 12 noww[++cnt]=p[f]; 13 goal[cnt]=t,p[f]=cnt; 14 noww[++cnt]=p[t]; 15 goal[cnt]=f,p[t]=cnt; 16 } 17 void Getcore(int nde,int fth) 18 { 19 siz[nde]=1; int sizz=0; 20 for(int i=p[nde];i;i=noww[i]) 21 if(goal[i]!=fth) 22 { 23 Getcore(goal[i],nde); 24 siz[nde]+=siz[goal[i]]; 25 Maxi(sizz,siz[goal[i]]); 26 } 27 Maxi(sizz,tot-siz[nde]); 28 if(sizz<maxs) maxs=sizz,cor=nde; 29 } 30 void Getsize(int nde,int fth) 31 { 32 siz[nde]=1; 33 for(int i=p[nde];i;i=noww[i]) 34 if(goal[i]!=fth) 35 { 36 Getsize(goal[i],nde); 37 siz[nde]+=siz[goal[i]]; 38 } 39 } 40 void Getanc(int nde,int fth,int ace) 41 { 42 anc[nde]=ace; 43 for(int i=p[nde];i;i=noww[i]) 44 if(goal[i]!=fth) 45 Getanc(goal[i],nde,ace); 46 } 47 void Add(int nde,int l,int r,int pos,int tsk) 48 { 49 val[nde]+=pos*tsk,num[nde]+=tsk; 50 if(l!=r) 51 { 52 int mid=(l+r)>>1,ls=2*nde,rs=2*nde+1; 53 if(pos<=mid) Add(ls,l,mid,pos,tsk); 54 else Add(rs,mid+1,r,pos,tsk); 55 } 56 } 57 int Query(int nde,int l,int r,int lft) 58 { 59 if(lft<=0) return 0; 60 if(l==r) return (lft+l-1)/l; 61 else 62 { 63 int mid=(l+r)>>1,ls=2*nde,rs=2*nde+1; 64 if(lft<=val[rs]) return Query(rs,mid+1,r,lft); 65 else return Query(ls,l,mid,lft-val[rs])+num[rs]; 66 } 67 } 68 int main() 69 { 70 scanf("%d",&n); 71 for(int i=1;i<n;i++) 72 scanf("%d%d",&t1,&t2),Link(t1,t2); 73 tot=maxs=n,Getcore(1,0),Getsize(cor,0); //printf("%d ",cor); 74 for(int i=p[cor];i;i=noww[i]) 75 { 76 Add(1,1,n,siz[goal[i]],1); 77 Getanc(goal[i],cor,goal[i]); 78 } 79 for(int i=1;i<=n;i++) 80 if(i==cor) puts("0"); 81 else 82 { 83 Add(1,1,n,siz[anc[i]],-1); 84 if(i!=anc[i]) Add(1,1,n,siz[anc[i]]-siz[i],1); 85 printf("%d ",Query(1,1,n,(n+1)/2-siz[i])); 86 Add(1,1,n,siz[anc[i]],1); 87 if(i!=anc[i]) Add(1,1,n,siz[anc[i]]-siz[i],-1); 88 89 } 90 return 0; 91 }
T3
看(i207M的)题解,咕咕咕