#include<iostream> #include<cstring> #include<string> #include<cstdio> #include<queue> #define Reg register using namespace std; int n,front,stack[100050],len[2050]; char s[100050],ls[100050]; struct Tree {Tree *next[26],*fail;int rank;}; Tree *New() { Tree *p=new Tree; for(Reg int i=0;i<26;++i) p->next[i]=NULL; p->fail=NULL; p->rank=0; return p; } Tree *root=New(); Tree *pos[100050]; void Insert(char s[],int k) { int l=strlen(s); len[k]=l; Tree *p=root; for(Reg int i=0,x;i<l;++i) { x=s[i]-'a'; if(p->next[x]==NULL) p->next[x]=New(); p=p->next[x]; } p->rank=k; return; } void GetFail() { queue<Tree *> q; q.push(root); while(!q.empty()) { Tree *p=q.front(); q.pop(); for(Reg int i=0;i<26;++i) { if(p->next[i]==NULL) continue; if(p==root) p->next[i]->fail=root; else { Tree *tmp=p->fail; while(tmp!=NULL) { if(tmp->next[i]!=NULL) { p->next[i]->fail=tmp->next[i]; break; } tmp=tmp->fail; } if(tmp==NULL) p->next[i]->fail=root; } q.push(p->next[i]); } } return; } void GetAc(char s[]) { int l=strlen(s); Tree *p=root; for(Reg int i=0;i<l;++i) { stack[++front]=s[i]-'a'; int x=stack[front]; while(p!=NULL&&p->next[x]==NULL) p=p->fail; if(p==NULL) p=root; else { p=p->next[x]; if(p->rank>0) { front-=len[p->rank]; p=pos[i-len[p->rank]]; } } pos[i]=p; } for(Reg int i=1;i<=front;++i) printf("%c",(char)stack[i]+'a'); return; } int main() { scanf("%s",s); scanf("%d",&n); for(Reg int i=1;i<=n;++i) { scanf("%s",ls); Insert(ls,i); } GetFail(); GetAc(s); return 0; }
改正$AC$自动机代码($TLE 93$)
#include<iostream> #include<cstring> #include<string> #include<cstdio> #include<queue> #define Reg register using namespace std; int n,front,stack[1000050],len[1000050]; char s[1000050],ls[1000050]; struct Tree {Tree *next[26],*fail;int rank;}; Tree *New() { Tree *p=new Tree; for(Reg int i=0;i<26;++i) p->next[i]=NULL; p->fail=NULL; p->rank=0; return p; } Tree *root=New(); Tree *pos[1000050]; void Insert(char s[],int k) { int l=strlen(s); len[k]=l; Tree *p=root; for(Reg int i=0;i<l;++i) { int x=s[i]-'a'; if(p->next[x]==NULL) p->next[x]=New(); p=p->next[x]; } p->rank=k; return; } void GetFail() { queue<Tree *> q; q.push(root); root->fail=NULL; while(!q.empty()) { Tree *p=q.front(); q.pop(); for(Reg int i=0;i<26;++i) { if(p->next[i]==NULL) continue; if(p==root) p->next[i]->fail=root; else { Tree *tmp=p->fail; while(tmp!=NULL) { if(tmp->next[i]!=NULL) { p->next[i]->fail=tmp->next[i]; break; } tmp=tmp->fail; } if(tmp==NULL) p->next[i]->fail=root; } q.push(p->next[i]); } } return; } void GetAc(char s[]) { int l=strlen(s); Tree *p=root; pos[0]=root; for(Reg int i=0;i<l;++i) { stack[++front]=s[i]-'a'; int x=stack[front]; while(p->next[x]==NULL&&p!=root) p=p->fail; p=p->next[x]; if(p==NULL) p=root; pos[front]=p; if(p->rank>0) { front-=len[p->rank]; p=pos[front]; } } for(Reg int i=1;i<=front;++i) printf("%c",(char)stack[i]+'a'); return; } int main() { scanf("%s",s); scanf("%d",&n); for(Reg int i=1;i<=n;++i) { scanf("%s",ls); Insert(ls,i); } GetFail(); GetAc(s); return 0; }
正解$Trie$图代码($AC$)
#include<iostream> #include<cstring> #include<string> #include<cstdio> #include<queue> #define Reg register using namespace std; int n,front,stack[100050],len[100050]; char s[100050],ls[100050]; struct Tree {Tree *next[26],*fail;int rank;}; Tree *New() { Tree *p=new Tree; for(Reg int i=0;i<26;++i) p->next[i]=NULL; p->fail=NULL; p->rank=0; return p; } Tree *root=New(); Tree *pos[100050]; void Insert(char s[],int k) { int l=strlen(s); len[k]=l; Tree *p=root; for(Reg int i=0;i<l;++i) { int x=s[i]-'a'; if(p->next[x]==NULL) p->next[x]=New(); p=p->next[x]; } p->rank=k; return; } void GetFail() { queue<Tree *> q; q.push(root); while(!q.empty()) { Tree *p=q.front(); q.pop(); for(Reg int i=0;i<26;++i) { if(p->next[i]!=NULL) { if(p==root) p->next[i]->fail=p; else p->next[i]->fail=p->fail->next[i]; q.push(p->next[i]); } else { if(p==root) p->next[i]=p; else p->next[i]=p->fail->next[i]; } } } return; } void GetAc(char s[]) { int l=strlen(s); Tree *p=root; pos[0]=root; for(Reg int i=0;i<l;++i) { stack[++front]=s[i]-'a'; int x=stack[front]; p=p->next[x]; pos[front]=p; if(p->rank>0) { front-=len[p->rank]; p=pos[front]; } } for(Reg int i=1;i<=front;++i) printf("%c",(char)stack[i]+'a'); return; } int main() { scanf("%s",s); scanf("%d",&n); for(Reg int i=1;i<=n;++i) { scanf("%s",ls); Insert(ls,i); } GetFail(); GetAc(s); return 0; }
其实改动不大,就是$Trie$图没有看过。
#include<iostream> #include<cstring> #include<cstdio> #define Reg register using namespace std; int n,m,p,t,tot,front,back,las[1550],du[1550]; double ans,g[1550],dis[1050][1050],dp[1050][1050],stack[1050]; struct Tu {int st,ed,next;} lian[100050]; void add(int x,int y) { lian[++tot].st=x; lian[tot].ed=y; lian[tot].next=las[x]; las[x]=tot; ++du[x]; return; } void dfs(int x) { g[x]=1; for(Reg int i=las[x];i;i=lian[i].next) { dfs(lian[i].ed); g[x]+=(double)g[lian[i].ed]/du[x]; } return; } void erfen(int p,int l,int r,int sl,int sr) { int mid=(l+r)/2,k; for(Reg int i=max(sl,mid);i<=sr;++i) { if(dp[i][p-1]+dis[mid][i]<dp[mid][p]) { dp[mid][p]=dp[i][p-1]+dis[mid][i]; k=i; } } if(l<=mid-1) erfen(p,l,mid-1,sl,k); if(mid+1<=r) erfen(p,mid+1,r,k,sr); return; } int main() { scanf("%d",&t); while(t--) { memset(las,0,sizeof(las)); memset(dis,0,sizeof(dis)); memset(du,0,sizeof(du)); memset(dp,0x7f,sizeof(dp)); tot=0; scanf("%d%d%d",&n,&m,&p); for(Reg int i=1,x,y;i<=m-n;++i) { scanf("%d%d",&x,&y); add(x,y); } for(Reg int i=1;i<=n-1;++i) ++du[i]; for(Reg int i=1;i<=n;++i) {dfs(i); --g[i];} for(Reg int i=1;i<=n;++i) { dis[i][i]=0; for(Reg int j=i+1;j<=n;++j) dis[i][j]=(double)dis[i][j-1]*du[j-1]+du[j-1]+g[j-1]*du[j-1]; } dp[n][1]=0; for(Reg int i=2;i<=p;++i) erfen(i,1,n,1,n); printf("%0.4lf\n",dp[1][p]); } return 0; }
首先用$g[x]$表示错误节点读档的期望步数,
$g[x]=1+\sum (\frac{g[son[x]]}{du[x]})$,叶子节点就是$1$。
然后用$dis[i][j]$表示正确节点 $i$到$j$的期望步数。
$dis[i][j]=dis[i][j-1]+1+\sum (\frac {g[son[j-1]]+dis[i][j]}{du[j-1]})$。
然后移项,$dis[i][j]=dis[i][j-1]\times du[j-1]+du[j-1]+g[j-1]\times du[j-1]$。
用$dis[i-1][j]$减$dis[i][j]$可得:
$dis[i-1][j]-dis[i][j]=(dis[i-1][j-1]-dis[i][j-1])\times du[j-1]$。
因为$du[j-1]>=1$,所以$dis[i-1][j]-dis[i][j]>=dis[i-1][j-1]-dis[i][j-1]$。
然后变一下,$dis[i+1][j]-dis[i][j]<=dis[i+1][j-1]-dis[i][j-1]$。
用$dp[i][k]$表示用了$k$次存档次数且在$i$存档时$i$到$n$的最短距离。
显然$dp[n][1]=0$,$dp[i][k]=\min (dp[j][k-1]+dis[i][j]),j>i$。
首先这个题存在一个推论:若$k$处取最优,那么当i增大时取最优的时侯一定是大于等于$k$的。
证明:
假设用了$k$次存档次数时 当$j=m$时,$dp[i][k]=dp[m][k-1]+dis[i][m]$取最小。
那么假设$l<m<r$,那么$dp[l][k-1]+dis[i][l]>dp[m][k-1]+dis[i][m]$,
且$dp[r][k-1]+dis[i][r]>dp[m][k-1]+dis[i][m]$。
当$i$增大$1$时,也就是$dp[i+1][k]$,
同样 $dp[i+1][k]=\min (dp[j][k-1]+dis[i+1][j])$。
那么我们比较$l$、$m$、$r$时的值。
由上面得:$dp[l][k-1]+dis[i][l]>dp[m][k-1]+dis[i][m]$,
那么当$i$又增大$1$时 只需要证明 $dp[l][k-1]+dis[i+1][l]>dp[m][k-1]+dis[i+1][m]$,
又由上面我们推出的式子 $dis[i+1][j]-dis[i][j]<=dis[i+1][j-1]-dis[i][j-1]$,
也就是说如果$i$增加$1$,$l$的增加量要比$m$大($l$的减小量要比$m$小),那么$l$会更大。
所以$l<m$,那么l始终不可能是最优解。
以此类推,则得到上面的推论。
之后就可以用二分求解,
定义$erfen(p,l,r,sl,sr)$。
先从$sl$到$sr$枚举一边,找到使$mid$最优的$k$,
然后递归地找$erfen(p,l,mid-1,sl,k)$,
$erfen(p,mid+1,r,k,sr)$。
时间复杂度$O(pnlogn)$。
这个题其实就是板子+板子+板子,
看$z$的范围比$m$、$n$的范围要大得多,所以可以先离散化减少复杂度,
需要树上差分,每个节点动态建一颗线段树,维护区间内物品数量的最大值,
树上差分时只要两个端点加$1$,$lca$减$1$,$fat[lca]$减$1$。
最后倒着一遍$dfs$,$merge+ask$。
考试代码($WA 0$)
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #define Reg register using namespace std; int n,m,tot,sum,size,fir[100050],las[100050],vis[100050],fat[100050][25],ins[100050]; int que1[100050],que2[100050],que3[100050],lss[100050],ans[100050]; struct Tu {int st,ed,val,next;} lian[200050]; struct Tree {int lch,rch,date;} tree[8000050]; void add(int x,int y) { lian[++tot].st=x; lian[tot].ed=y; lian[tot].next=las[x]; las[x]=tot; return; } void dfs(int x) { vis[x]=1; for(Reg int j=1;j<=20;++j) fat[x][j]=fat[fat[x][j-1]][j-1]; for(Reg int i=las[x];i;i=lian[i].next) { if(!vis[lian[i].ed]) { fat[lian[i].ed][0]=x; ins[lian[i].ed]=ins[x]+1; dfs(lian[i].ed); } } return; } int lca(int x,int y) { if(ins[x]<ins[y]) swap(x,y); if(ins[x]!=ins[y]) { for(Reg int i=20;i>=0;--i) if(ins[fat[x][i]]>ins[y]) x=fat[x][i]; if(x==y) return x; x=fat[x][0]; if(x==y) return x; } if(x==y) return x; for(Reg int i=20;i>=0;--i) { if(fat[x][i]!=fat[y][i]) { x=fat[x][i]; y=fat[y][i]; } } return fat[x][0]; } int add_num(int k,int l,int r,int pos,int val) { if(!k) k=++size; if(l==r) { tree[k].date+=val; return k; } int mid=(l+r)/2; if(pos<=mid) tree[k].lch=add_num(tree[k].lch,l,mid,pos,val); else tree[k].rch=add_num(tree[k].rch,mid+1,r,pos,val); tree[k].date=max(tree[tree[k].lch].date,tree[tree[k].rch].date); return k; } void work(int l,int r,int num) { int f=lca(l,r); fir[l]=add_num(fir[l],1,sum,num,1); fir[r]=add_num(fir[r],1,sum,num,1); fir[f]=add_num(fir[f],1,sum,num,-1); if(fat[f][0]) fir[fat[f][0]]=add_num(fir[fat[f][0]],1,sum,num,-1); return; } int merge(int x,int y,int l,int r) { tree[0].date=-0x7fffffff; if(!x||!y) return x+y; if(l==r) { tree[x].date+=tree[y].date; return x; } int mid=(l+r)/2; if(l<=mid) tree[x].lch=merge(tree[x].lch,tree[y].lch,l,mid); if(mid+1<=r) tree[x].rch=merge(tree[x].rch,tree[y].rch,mid+1,r); tree[x].date=max(tree[tree[x].lch].date,tree[tree[x].rch].date); // cout<<l<<' '<<r<<" "<<tree[x].lch<<' '<<tree[x].rch<<" "<<tree[tree[x].lch].date<<' '<<tree[tree[x].rch].date<<" "<<tree[x].date<<endl; return x; } int ask(int x,int l,int r) { if(l==r) return l; int mid=(l+r)/2; if(tree[tree[x].lch].date<=0&&tree[tree[x].rch].date<=0) return 0; if(tree[x].lch&&tree[tree[x].lch].date>tree[tree[x].rch].date) return ask(tree[x].lch,l,mid); else if(tree[x].lch&&tree[tree[x].lch].date==tree[tree[x].rch].date) return ask(tree[x].lch,l,mid); else if(tree[x].rch&&tree[tree[x].lch].date<tree[tree[x].rch].date) return ask(tree[x].rch,mid+1,r); else return 0; } int aaa(int x,int l,int r,int num) { if(!x) return 0; if(l==r) return tree[x].date; int mid=(l+r)/2; if(num<=mid) return aaa(tree[x].lch,l,mid,num); else return aaa(tree[x].rch,mid+1,r,num); } void dfs_re(int x) { vis[x]=1; for(Reg int i=las[x];i;i=lian[i].next) { if(!vis[lian[i].ed]) { dfs_re(lian[i].ed); fir[x]=merge(fir[x],fir[lian[i].ed],1,sum); } } ans[x]=ask(fir[x],1,sum); // cout<<x<<" "; // for(Reg int i=1;i<=sum;++i) // { // printf("%d ",aaa(fir[x],1,sum,i)); // } // cout<<endl; return; } int main() { scanf("%d%d",&n,&m); tree[0].date=-0x7fffffff; for(Reg int i=1,x,y;i<=n-1;++i) { scanf("%d%d",&x,&y); add(x,y); add(y,x); } ins[1]=1; dfs(1); for(Reg int i=1,x,y,z;i<=m;++i) { scanf("%d%d%d",&que1[i],&que2[i],&que3[i]); lss[i]=que3[i]; } sort(lss+1,lss+m+1,less<int>()); sum=unique(lss+1,lss+m+1)-lss-1; for(Reg int i=1;i<=m;++i) que3[i]=lower_bound(lss+1,lss+m+1,que3[i])-lss; for(Reg int i=1;i<=m;++i) work(que1[i],que2[i],que3[i]); memset(vis,0,sizeof(vis)); dfs_re(1); for(Reg int i=1;i<=n;++i) printf("%d\n",lss[ans[i]]); return 0; }
$AC$代码($AC$)
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #define Reg register using namespace std; int n,m,tot,sum,size,fir[100050],las[100050],vis[100050],fat[100050][25],ins[100050]; int que1[100050],que2[100050],que3[100050],lss[100050],ans[100050]; struct Tu {int st,ed,val,next;} lian[200050]; struct Tree {int lch,rch,date;} tree[8000050]; void add(int x,int y) { lian[++tot].st=x; lian[tot].ed=y; lian[tot].next=las[x]; las[x]=tot; return; } void dfs(int x) { vis[x]=1; for(Reg int j=1;j<=20;++j) fat[x][j]=fat[fat[x][j-1]][j-1]; for(Reg int i=las[x];i;i=lian[i].next) { if(!vis[lian[i].ed]) { fat[lian[i].ed][0]=x; ins[lian[i].ed]=ins[x]+1; dfs(lian[i].ed); } } return; } int lca(int x,int y) { if(ins[x]<ins[y]) swap(x,y); if(ins[x]!=ins[y]) { for(Reg int i=20;i>=0;--i) if(ins[fat[x][i]]>ins[y]) x=fat[x][i]; if(x==y) return x; x=fat[x][0]; if(x==y) return x; } if(x==y) return x; for(Reg int i=20;i>=0;--i) { if(fat[x][i]!=fat[y][i]) { x=fat[x][i]; y=fat[y][i]; } } return fat[x][0]; } int add_num(int k,int l,int r,int pos,int val) { if(!k) k=++size; if(l==r) { tree[k].date+=val; return k; } int mid=(l+r)/2; if(pos<=mid) tree[k].lch=add_num(tree[k].lch,l,mid,pos,val); else tree[k].rch=add_num(tree[k].rch,mid+1,r,pos,val); tree[k].date=max(tree[tree[k].lch].date,tree[tree[k].rch].date); return k; } void work(int l,int r,int num) { int f=lca(l,r); fir[l]=add_num(fir[l],1,sum,num,1); fir[r]=add_num(fir[r],1,sum,num,1); fir[f]=add_num(fir[f],1,sum,num,-1); if(fat[f][0]) fir[fat[f][0]]=add_num(fir[fat[f][0]],1,sum,num,-1); return; } int merge(int x,int y,int l,int r) { tree[0].date=-0x7fffffff; if(!x||!y) return x+y; if(l==r) { tree[x].date+=tree[y].date; return x; } int mid=(l+r)/2; if(l<=mid) tree[x].lch=merge(tree[x].lch,tree[y].lch,l,mid); if(mid+1<=r) tree[x].rch=merge(tree[x].rch,tree[y].rch,mid+1,r); tree[x].date=max(tree[tree[x].lch].date,tree[tree[x].rch].date); // cout<<l<<' '<<r<<" "<<tree[x].lch<<' '<<tree[x].rch<<" "<<tree[tree[x].lch].date<<' '<<tree[tree[x].rch].date<<" "<<tree[x].date<<endl; return x; } int ask(int x,int l,int r) { if(l==r) return l; int mid=(l+r)/2; if(tree[tree[x].lch].date<=0&&tree[tree[x].rch].date<=0) return 0; if(tree[x].lch&&tree[tree[x].lch].date>tree[tree[x].rch].date) return ask(tree[x].lch,l,mid); else if(tree[x].lch&&tree[tree[x].lch].date==tree[tree[x].rch].date) return ask(tree[x].lch,l,mid); else if(tree[x].rch&&tree[tree[x].lch].date<tree[tree[x].rch].date) return ask(tree[x].rch,mid+1,r); else return 0; } int aaa(int x,int l,int r,int num) { if(!x) return 0; if(l==r) return tree[x].date; int mid=(l+r)/2; if(num<=mid) return aaa(tree[x].lch,l,mid,num); else return aaa(tree[x].rch,mid+1,r,num); } void dfs_re(int x) { vis[x]=1; for(Reg int i=las[x];i;i=lian[i].next) { if(!vis[lian[i].ed]) { dfs_re(lian[i].ed); fir[x]=merge(fir[x],fir[lian[i].ed],1,sum); } } ans[x]=ask(fir[x],1,sum); return; } int main() { scanf("%d%d",&n,&m); tree[0].date=-0x7fffffff; for(Reg int i=1,x,y;i<=n-1;++i) { scanf("%d%d",&x,&y); add(x,y); add(y,x); } ins[1]=1; dfs(1); for(Reg int i=1,x,y,z;i<=m;++i) { scanf("%d%d%d",&que1[i],&que2[i],&que3[i]); lss[i]=que3[i]; } sort(lss+1,lss+m+1,less<int>()); sum=unique(lss+1,lss+m+1)-lss-1; for(Reg int i=1;i<=m;++i) que3[i]=lower_bound(lss+1,lss+sum+1,que3[i])-lss; for(Reg int i=1;i<=m;++i) work(que1[i],que2[i],que3[i]); memset(vis,0,sizeof(vis)); dfs_re(1); for(Reg int i=1;i<=n;++i) printf("%d\n",lss[ans[i]]); return 0; }
这个题就尴尬了,离散化时的$lower\_ bound$函数里写错了一个变量,导致改了这个题一个下午。。
考试开始时先看的$T1$,发现$T1$就是个板子。。。
然后花了$20$分钟打了$T1$,考完试发现自己拿了$40$分,程序中一个地方写错了。
溜了一眼$T2$,发现$T2$是个$DP$题,也没什么思路,就跳过了。
后面基本上都在打$T3$,$T3$是个数据结构题,感觉我的线段树还是比较熟练的,但是调代码就恶心了。。。
打$T3$用了一个小时,调这道题基本上也用了一个小时,最后还没调过来,考完试才发现当时没调离散化,有$1$个变量打错了。
总之,调代码是个恶心的活。。。但是调完代码如果过了就会非常愉悦。。。
还有就是板子要记熟,不要考试时因为这个吃亏。