6/14考试总结
实锤:我就是一个超级大垃圾!!!
必须从这个视角俯瞰自己了啊
T1:Censoring
FJ把杂志上所有的文章摘抄了下来并把它变成了一个长度不超过1e5的字符串S。他有一个包含n个单词的列表,列表里的n个单词记为ti,他希望从S中删除这些单词。
FJ每次在S中找到最早出现的列表中的单词(最早出现指该单词的开始位置最小),然后从S中删除这个单词。他重复这个操作直到S中没有列表里的单词为止。注意删除一个单词后可能会导致S中出现另一个列表中的单词
FJ注意到列表中的单词不会出现一个单词是另一个单词子串的情况,这意味着每个列表中的单词在S中出现的开始位置是互不相同的
请帮助FJ完成这些操作并输出最后的S。第一行包含一个字符串S
第二行包含一个整数N N<2000 接下来的N行,每行包含一个字符串i1...tNt_NtN。他希望从S中删除这些单词。
FJ每次在S中找到最早出现的列表中的单词(最早出现指该单词的开始位置最小),然后从S中删除这个单词。他重复这个操作直到S中没有列表里的单词为止。注意删除一个单词后可能会导致S中出现另一个列表中的单词
FJ注意到列表中的单词不会出现一个单词是另一个单词子串的情况,这意味着每个列表中的单词在S中出现的开始位置是互不相同的
请帮助FJ完成这些操作并输出最后的S5的字符串S。他有一个包含n个单词的列表,列表里的n个单词记为t1t_1t1...tNt_NtN。他希望从S中删除这些单词。
FJ每次在S中找到最早出现的列表中的单词(最早出现指该单词的开始位置最小),然后从S中删除这个单词。他重复这个操作直到S中没有列表里的单词为止。注意删除一个单词后可能会导致S中出现另一个列表中的单词
FJ注意到列表中的单词不会出现一个单词是另一个单词子串的情况,这意味着每个列表中的单词在S中出现的开始位置是互不相同的
请帮助FJ完成这些操作并输出最后的S
一本通水题,AC自动机+栈,hash也能过,过多的回溯fail指针导致T,40。
AC自动机尝试匹配时需要通过fail指针进行回溯,所以在开始匹配前处理好结束标记就行。
利用好数组模拟栈的特性,不要一个一个pop。
1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 char s[100005],word[100005]; 5 int n,cnt,last[100005],bj[100005],t[100005][26],fail[100005],fin[100005]; 6 int q[100005],q1,q2,leng,stack[100005],top; 7 void insert(char *ss){ 8 int p=0,len=strlen(ss); 9 for(int i=0;i<len;++i) 10 if(t[p][ss[i]-'a'])p=t[p][ss[i]-'a']; 11 else p=t[p][ss[i]-'a']=++cnt; 12 fin[p]=len; 13 } 14 void bfs(){ 15 q1=1;for(int i=0;i<=25;++i)if(t[0][i])q[++q2]=t[0][i]; 16 for(;q1<=q2;++q1)for(int i=0;i<=25;++i) 17 if(t[q[q1]][i])fail[q[++q2]=t[q[q1]][i]]=t[fail[q[q1]]][i]; 18 else t[q[q1]][i]=t[fail[q[q1]]][i]; 19 } 20 void find(){ 21 int p=0,k; 22 for(int i=0;i<leng;++i){ 23 k=last[i]=p=t[p][s[i]-'a'];stack[++top]=i; 24 while(k){ 25 if(fin[k]){ 26 p=last[stack[top-fin[k]+1]-1]; 27 for(int ii=1;ii<=fin[k];++ii)bj[stack[top]]=1,top--; 28 break; 29 } 30 k=fail[k]; 31 } 32 } 33 } 34 int main(){ 35 //freopen("cen.in","r",stdin); 36 //freopen("cen.out","w",stdout); 37 scanf("%s%d",s,&n);leng=strlen(s); 38 for(int i=1;i<=n;++i)scanf("%s",word),insert(word); 39 bfs();find(); 40 for(int i=0;i<leng;++i)if(!bj[i])putchar(s[i]); 41 }
1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 char s[100005],word[100005]; 5 int n,cnt,last[100005],bj[100005],t[100005][26],fail[100005],fin[100005]; 6 int q[100005],q1,q2,leng,stack[100005],top; 7 void insert(char *ss){ 8 int p=0,len=strlen(ss); 9 for(int i=0;i<len;++i) 10 if(t[p][ss[i]-'a'])p=t[p][ss[i]-'a']; 11 else p=t[p][ss[i]-'a']=++cnt; 12 fin[p]=len; 13 } 14 void bfs(){ 15 q1=1;for(int i=0;i<=25;++i)if(t[0][i])q[++q2]=t[0][i]; 16 for(;q1<=q2;++q1)for(int i=0;i<=25;++i) 17 if(t[q[q1]][i])fail[q[++q2]=t[q[q1]][i]]=t[fail[q[q1]]][i]; 18 else t[q[q1]][i]=t[fail[q[q1]]][i]; 19 } 20 void find(){ 21 int k=0; 22 for(int i=0;i<leng;++i){ 23 k=last[i]=t[k][s[i]-'a'];stack[++top]=i; 24 if(fin[k]){ 25 top-=fin[k]; 26 k=last[stack[top]]; 27 28 } 29 } 30 } 31 int main(){ 32 scanf("%s%d",s,&n);leng=strlen(s); 33 for(int i=1;i<=n;++i)scanf("%s",word),insert(word); 34 bfs();find(); 35 for(int i=1;i<=top;++i)putchar(s[stack[i]]); 36 }
T2:记忆的轮廓
一颗树,根节点编号为1,目标节点编号为n,其中1-n的简单路径上,编号依次递增,在[1,n]中,一共有n个节点。我们把编号在[1,n]的叫做正确节点,[n+1,m]的叫做错误节点。一个叶子,如果是正确节点则为正确叶子,否则称为错误叶子。莎缇拉要帮助昴到达贤者之塔,因此现在面临着存档位置设定的问题。
为了让昴成长为英雄,因此一共只有p次存档的机会,其中1和n必须存档。被莎缇拉设置为要存档的节点称为存档位置。当然不能让昴陷入死循环,所以存档只能在正确节点上进行,而且同一个节点不能存多次档。因为通往贤者之塔的路上有影响的瘴气,因此莎缇拉假设昴每次位于树上一个节点时,都会等概率选择一个儿子走下去。每当走到一个错误叶子时,再走一步就会读档。具体的,每次昴到达一个新的存档位置,存档点便会更新为这个位置(假如现在的存档点是i,现在走到了一个存档位置j>i,那么存档点便会更新为j)。读档的意思就是回到当前存档点。初始昴位于1,当昴走到正确节点n时,便结束了路程。莎缇拉想知道,最优情况下,昴结束路程的期望步数是多少?50<=p<=n<=700,m<=1500,T<=5。数据保证每个正确节点均有至少2个儿子,至多3个儿子。
全场爆零,数据范围没粘梯度,50%很好拿的也没人拿到,而我有看错了题目,0。
考场上其实已经想到了70%思路,但毕竟读错题了。
其中一种正解很抖机灵,存档点相距太远答案就会爆炸增长而存档机会又不少,设不会在相距40以上的点存档,就能把70变成AC
题解也有很多种,每种都值得学习。
1 #include<cstdio> 2 int n,m,p,ext[705],a,t,b; 3 double times[705],dt[705][705],dp[705][705]; 4 inline double min(double a,double b){return a<b?a:b;} 5 int main(){ 6 //freopen("memory.in","r",stdin); 7 //freopen("memory.out","w",stdout); 8 scanf("%d",&t); 9 while(t--){ 10 scanf("%d%d%d",&n,&m,&p); 11 for(int i=1;i<=n;++i)ext[i]=0; 12 for(int i=1;i<=m-n;++i){ 13 scanf("%d%d",&a,&b); 14 if(a<=n)ext[a]++; 15 if(b<=n)ext[b]++; 16 } 17 for(int i=1;i<=n;++i)ext[i]++; 18 for(int i=2;i<=n;++i){ 19 times[i]=1; 20 for(int j=i-1;j;--j) 21 times[j]=times[j+1]*ext[j],dt[j][i]=dt[j+1][i]+times[j]/ext[j]+times[j]*2*(ext[j]-1)/ext[j]; 22 } 23 for(int i=1;i<=700;++i)for(int j=1;j<=700;++j)dp[i][j]=1e10; 24 dp[1][1]=0; 25 for(int i=1;i<n;++i)for(int j=1;j<=i;++j)for(int k=i+1;k<=n;++k)dp[k][j+1]=min(dp[k][j+1],dp[i][j]+dt[i][k]); 26 printf("%.4lf",dp[n][p]); 27 } 28 29 }
1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 #define mm(a) memset(a,0,sizeof(a)) 5 int n,m,p,a,b,t;double dt[1505][1505],g[1505],tot[1505],dp[1505][1505];int fir[1505],to[1530],l[1530],cnt,s[1505]; 6 inline double min(double a,double b){return a<b?a:b;} 7 void connect(int a,int b){l[++cnt]=fir[a];fir[a]=cnt;to[cnt]=b;} 8 void dfs(int p){dt[p][p]=0; 9 for(int i=fir[p];i;i=l[i]){ 10 dfs(to[i]),s[p]++; 11 if(p>n)g[p]+=g[to[i]]; 12 } 13 if(p>n)if(s[p])g[p]=g[p]/s[p]+1;else g[p]=1; 14 if(p<=n)for(int i=fir[p];i;i=l[i])tot[p]+=g[to[i]]; 15 } 16 int main(){//freopen("1.in","r",stdin); 17 scanf("%d",&t); 18 while(t--){ 19 scanf("%d%d%d",&n,&m,&p); 20 cnt=0;mm(tot);mm(g);mm(s);mm(fir); 21 for(int i=1;i<=n;++i)for(int ap=1;ap<=p;++ap)dp[i][ap]=1e20;dp[1][1]=0; 22 for(int i=1;i<=m-n;++i) 23 scanf("%d%d",&a,&b),connect(a,b); 24 for(int i=1;i<n;++i)connect(i,i+1); 25 dfs(1); 26 for(int i=1;i<=n;++i) 27 for(int j=i+1;j<=i+40&&j<=n;++j) 28 dt[i][j]=dt[i][j-1]*s[j-1]+s[j-1]+tot[j-1]; 29 for(int i=2;i<=n;++i) 30 for(int j=((i-40>=1)?i-40:1);j<i;++j) 31 for(int ap=2;ap<=i;++ap) 32 dp[i][ap]=min(dp[i][ap],dp[j][ap-1]+dt[j][i]); 33 printf("%.4lf\n",dp[n][p]); 34 } 35 }
T3:雨天的尾巴
N个点,形成一个树状结构。有M次发放,每次选择两个点x,y对于x到y的路径上(含x,y)每个点发一袋Z类型的物品。完成所有发放后,每个点存放最多的是哪种物品。
1<=N,M<=100000,1<=a,b,x,y<=N,1<=z<=1e99
树上权值线段树动态开点+树上差分,考场上想出了树上差分可是不会弄,纯暴力50。
差分是从下往上,不是从根往下捯!这回彻底记住了。
改的时候线段树合并打错。。。忘了直接继承节点了而是此次都新建,M->R->M->R...
1 #include<cstdio> 2 #include<map> 3 using namespace std; 4 int f[5001][13],fir[5001],l[10001],to[10001],cnt,dep[10001],res; 5 int ta[100001],tb[100001],tk[100001],lwb[5001],kind,a,b,LCA,n,mm; 6 int have[5001][5001],ref[5001]; 7 map<int,int>m; 8 void connect(int a,int b){ 9 l[++cnt]=fir[a];fir[a]=cnt;to[cnt]=b; 10 l[++cnt]=fir[b];fir[b]=cnt;to[cnt]=a; 11 } 12 void dfs(int p){ 13 for(int i=1;i<=12;++i)f[p][i]=f[f[p][i-1]][i-1]; 14 for(int i=fir[p];i;i=l[i])if(to[i]!=f[p][0])f[to[i]][0]=p,dep[to[i]]=dep[p]+1,dfs(to[i]); 15 } 16 int lca(int a,int b){ 17 if(dep[a]<dep[b])res=a,a=b,b=res; 18 for(int dt=dep[a]-dep[b];dt;dt-=dt&-dt)a=f[a][lwb[dt&-dt]]; 19 if(a==b)return a; 20 for(int i=12;i>=0;--i)if(f[a][i]!=f[b][i])a=f[a][i],b=f[b][i]; 21 return f[a][0]; 22 } 23 int main(){ 24 //freopen("tail.in","r",stdin); 25 //freopen("tail.out","w",stdout); 26 for(int i=0;i<=12;++i)lwb[1<<i]=i; 27 scanf("%d%d",&n,&mm); 28 for(int i=1;i<n;++i)scanf("%d%d",&a,&b),connect(a,b); 29 for(int i=1;i<=mm;++i){ 30 scanf("%d%d%d",&ta[i],&tb[i],&tk[i]); 31 if(m.find(tk[i])==m.end())m[tk[i]]=++kind,ref[kind]=tk[i]; 32 tk[i]=m[tk[i]]; 33 } 34 dfs(1); 35 for(int i=1;i<=mm;++i){ 36 LCA=lca(ta[i],tb[i]); 37 for(int ii=ta[i];ii!=LCA;ii=f[ii][0])have[ii][tk[i]]++; 38 for(int ii=tb[i];ii!=LCA;ii=f[ii][0])have[ii][tk[i]]++; 39 have[LCA][tk[i]]++; 40 } 41 for(int i=1;i<=n;++i){ 42 int ans=0; 43 for(int j=1;j<=kind;++j)if(have[i][j]>have[i][ans]||(have[i][j]==have[i][ans]&&ref[j]<ref[ans]))ans=j; 44 printf("%d\n",ref[ans]); 45 } 46 }
1 #include<cstdio> 2 #include<map> 3 #include<algorithm> 4 using namespace std; 5 inline int max(int a,int b){return a>b?a:b;} 6 int f[100001][19],fir[100001],l[200001],to[200001],cntt,dep[100001],res; 7 int ta[100001],tb[100001],tk[100001],lwb[300001],kind,a,b,LCA,n,mm; 8 int ref[100001],ans[100001]; 9 int w[15000000],lc[15000000],rc[15000000],cnt=100000; 10 int bf[100001]; 11 map<int,int>m; 12 void connect(int a,int b){ 13 l[++cntt]=fir[a];fir[a]=cntt;to[cntt]=b; 14 l[++cntt]=fir[b];fir[b]=cntt;to[cntt]=a; 15 } 16 void dfs(int p){ 17 for(int i=1;i<=18;++i)f[p][i]=f[f[p][i-1]][i-1]; 18 for(int i=fir[p];i;i=l[i])if(to[i]!=f[p][0])f[to[i]][0]=p,dep[to[i]]=dep[p]+1,dfs(to[i]); 19 } 20 int lca(int a,int b){ 21 if(dep[a]<dep[b])res=a,a=b,b=res; 22 for(int dt=dep[a]-dep[b];dt;dt-=dt&-dt)a=f[a][lwb[dt&-dt]]; 23 if(a==b)return a; 24 for(int i=18;i>=0;--i)if(f[a][i]!=f[b][i])a=f[a][i],b=f[b][i]; 25 return f[a][0]; 26 } 27 void add(int &p,int l,int r,int pos,int ww){ 28 if(!p)p=++cnt; 29 if(l==r){w[p]+=ww;return;} 30 int mid=l+r>>1; 31 if(pos>mid)add(rc[p],mid+1,r,pos,ww); 32 else add(lc[p],l,mid,pos,ww); 33 w[p]=max((lc[p]?w[lc[p]]:0),(rc[p]?w[rc[p]]:0)); 34 } 35 int find_ans(int p,int l,int r){ 36 if(!w[p])return 0; 37 if(l==r)return l; 38 int mid=l+r>>1; 39 if(lc[p]&&w[p]==w[lc[p]])return find_ans(lc[p],l,mid); 40 else return find_ans(rc[p],mid+1,r); 41 } 42 void merge(int p,int l,int r,int &fa){ 43 int mid=l+r>>1;if(!fa)fa=++cnt,printf("%d %d %d %d\n",p,l,r,cnt); 44 if(l==r){w[fa]+=w[p];return;} 45 if(lc[p])if(lc[fa])merge(lc[p],l,mid,lc[fa]);else lc[fa]=lc[p]; 46 if(rc[p])if(rc[fa])merge(rc[p],mid+1,r,rc[fa]);else rc[fa]=rc[p]; 47 w[fa]=max((lc[fa]?w[lc[fa]]:0),(rc[fa]?w[rc[fa]]:0)); 48 } 49 void dfs_ans(int p){ 50 for(int i=fir[p];i;i=l[i])if(to[i]!=f[p][0])dfs_ans(to[i]); 51 ans[p]=find_ans(p,1,kind);if(f[p][0])merge(p,1,kind,f[p][0]); 52 } 53 int main(){ 54 for(int i=0;i<=18;++i)lwb[1<<i]=i; 55 scanf("%d%d",&n,&mm); 56 for(int i=1;i<n;++i)scanf("%d%d",&a,&b),connect(a,b); 57 for(int i=1;i<=mm;++i)scanf("%d%d%d",&ta[i],&tb[i],&tk[i]),bf[i]=tk[i]; 58 sort(bf+1,bf+1+mm); 59 for(int i=1;i<=mm;++i)if(m.find(bf[i])==m.end())m[bf[i]]=++kind,ref[kind]=bf[i]; 60 for(int i=1;i<=mm;++i)tk[i]=m[tk[i]]; 61 dfs(1); 62 for(int i=1;i<=mm;++i){ 63 LCA=lca(ta[i],tb[i]); 64 add(ta[i],1,kind,tk[i],1);add(tb[i],1,kind,tk[i],1); 65 add(LCA,1,kind,tk[i],-1);add(f[LCA][0],1,kind,tk[i],-1); 66 } 67 dfs_ans(1); 68 for(int i=1;i<=n;++i)printf("%d\n",ref[ans[i]]); 69 }
你很弱诶,你真的很弱诶。啥都不会打,暴力骗分总分还干不过T1一个hash。
考场上心态太自信了吧,T1打完过样例之后又手模了4,5组都没问题。就往后走了
T2,T3寻思着T1应该稳了,100分rank也不低就飘了。
平时打代码要注重理解。。。不要着急赶进度,注重积累,记住每一个小知识点,谁知道什么时候会有用
难题可以不打,简单题必须AC,暴力对拍检查低错,先稳后强。
加油吧,还有机会,小心被干到第二机房。