20160619~20160625 8
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了20160619~20160625 8相关的知识,希望对你有一定的参考价值。
20160620
1、bzoj3172 http://www.lydsy.com/JudgeOnline/problem.php?id=3172
题意:某人读论文,一篇论文是由许多单词组成。但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次。注意论文中单词之间是有分隔的。单词数≤200,长度≤1000000
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define maxn 1000010 5 #define inc(i,j,k) for(int i=j;i<=k;i++) 6 using namespace std; 7 8 int ch[maxn][26],sum[maxn],pos[maxn],q[maxn],n,tot,fail[maxn]; char s[maxn]; 9 int insert(char *s){ 10 int l=strlen(s+1),x=0; 11 inc(i,1,l){if(!ch[x][s[i]-‘a‘])ch[x][s[i]-‘a‘]=++tot; x=ch[x][s[i]-‘a‘]; sum[x]++;} return x; 12 } 13 void getfail(){ 14 int l=1,r=0; inc(i,0,25)if(ch[0][i])q[++r]=ch[0][i],fail[ch[0][i]]=0; 15 while(l<=r){ 16 int x=q[l++]; 17 inc(i,0,25)if(ch[x][i]){ 18 int y=fail[x]; while(y&&!ch[y][i])y=fail[y]; 19 if(ch[y][i])fail[ch[x][i]]=ch[y][i]; q[++r]=ch[x][i]; 20 } 21 } 22 for(int i=r;i;i--)sum[fail[q[i]]]+=sum[q[i]]; 23 } 24 int main(){ 25 scanf("%d",&n); tot=0; inc(i,1,n){scanf("%s",s+1); pos[i]=insert(s);} 26 getfail(); inc(i,1,n)printf("%d\\n",sum[pos[i]]); 27 }
题解:先将每个单词插入trie,经过的节点的sum[i]++,然后求fail函数,求完后按BFS序倒过来维护sum[fail[i]]+=sum[i],最后输出第i个单词末尾字符节点的sum值。
2、bzoj1269 http://www.lydsy.com/JudgeOnline/problem.php?id=1269
题意:维护一个字符串,支持插入,删除,翻转操作。操作数≤100000,字符串长度≤2M。
代码:
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 #include <ext/rope> 5 #define maxn 2000000 6 #define inc(i,j,k) for(int i=j;i<=k;i++) 7 using namespace std; 8 using namespace __gnu_cxx; 9 10 inline int read(){ 11 char ch=getchar(); int f=1,x=0; 12 while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1; ch=getchar();} 13 while(ch>=‘0‘&&ch<=‘9‘)x=x*10+ch-‘0‘,ch=getchar(); 14 return f*x; 15 } 16 rope <char> l,r,tmp; char ls[maxn],rs[maxn],opt[10]; int n,now; 17 int main(){ 18 n=read(); 19 inc(i,1,n){ 20 scanf("%s",opt); 21 if(opt[0]==‘M‘)now=read(); if(opt[0]==‘P‘)now--; 22 if(opt[0]==‘N‘)now++; if(opt[0]==‘G‘)printf("%c\\n",l[now]); 23 if(opt[0]==‘D‘){int x=read(),len=l.length(); l.erase(now,x); r.erase(len-now-x,x);} 24 if(opt[0]==‘R‘){ 25 int x=read(),len=l.length(); tmp=l.substr(now,x); 26 l=l.substr(0,now)+r.substr(len-now-x,x)+l.substr(now+x,len-now-x); 27 r=r.substr(0,len-now-x)+tmp+r.substr(len-now,now); 28 } 29 if(opt[0]==‘I‘){ 30 int x=read(),len=l.length(); 31 inc(i,0,x-1){ 32 ls[i]=getchar(); while(ls[i]<32||ls[i]>126)ls[i]=getchar(); rs[x-i-1]=ls[i]; 33 } 34 ls[x]=rs[x]=0; l.insert(now,ls); r.insert(len-now,rs); 35 } 36 } 37 }
题解:C++有个库里面有个容器叫rope,可以实现可持久化平衡树,然而本题只要它的插入、删除、截取字符串功能就行了,翻转怎么办?维护一个倒序的rope即可。
20160621
3、bzoj1030 http://www.lydsy.com/JudgeOnline/problem.php?id=1030
题意:给出一个字典和一个长度,要求有多少个这个长度的字符串里含有子串为字典里的单词。字符串和字典里的字符都为大写字母。单词数≤60,字符串及单词长度≤100。
代码:
1 #include <cstring> 2 #include <cstdio> 3 #include <algorithm> 4 #include <queue> 5 #define maxn 10000 6 #define inc(i,j,k) for(int i=j;i<=k;i++) 7 #define mod 10007 8 using namespace std; 9 10 int n,m,sz,ch[maxn][26],fail[maxn],ans,f[150][maxn]; bool val[maxn]; 11 void insert(char *s){ 12 int x=0,len=strlen(s+1); inc(i,1,len){if(!ch[x][s[i]-‘A‘])ch[x][s[i]-‘A‘]=++sz; x=ch[x][s[i]-‘A‘];} 13 val[x]=1; 14 } 15 queue <int> q; 16 void getfail(){ 17 inc(i,0,25)if(ch[0][i])q.push(ch[0][i]),fail[ch[0][i]]=0; 18 while(!q.empty()){ 19 int x=q.front(); q.pop(); 20 inc(i,0,25)if(ch[x][i]){ 21 int y=fail[x]; while(y&&!ch[y][i])y=fail[y]; if(ch[y][i])fail[ch[x][i]]=ch[y][i]; 22 if(val[ch[y][i]])val[ch[x][i]]=1; q.push(ch[x][i]); 23 } 24 } 25 } 26 char str[150]; 27 int main(){ 28 scanf("%d%d",&n,&m); inc(i,1,n)scanf("%s",str+1),insert(str); ans=1; getfail(); 29 inc(i,1,m)ans=ans*26%mod; f[0][0]=1; 30 inc(i,1,m)inc(j,0,sz)if(!val[j]&&f[i-1][j]){ 31 inc(k,0,25){ 32 int x=j; while(x&&!ch[x][k])x=fail[x]; f[i][ch[x][k]]=(f[i][ch[x][k]]+f[i-1][j])%mod; 33 } 34 } 35 inc(i,0,sz)if(!val[i]){ 36 ans=(ans-f[m][i])%mod; if(ans<0)ans+=mod; 37 } 38 printf("%d",ans); return 0; 39 }
题解:在AC自动机上跑dp,求不含字典单词的个数,再用总个数减。f[i][j]表示当前处理第i个位置,在trie上的节点为j。f[i][j]=sum{f[i+1][ch[x][k]]},x为j的fail祖先,为k为大写字母,不能走到val为1的节点。注意val的处理,特别是getfail中的“if(val[ch[y][i]])val[ch[x][i]]=1;”一句。
20160622
4、bzoj2588 http://www.lydsy.com/JudgeOnline/problem.php?id=2588
题意:n点树,m个询问求点u到点v路径上第k小的点权。强制在线。n,m≤100000
代码:
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 #define inc(i,j,k) for(int i=j;i<=k;i++) 5 #define maxn 100500 6 using namespace std; 7 8 int f[18][maxn],rt[maxn],dep[maxn],ch[maxn*35][2],sz[maxn*35],v[maxn],id[maxn],n,m,size,last,tot; 9 struct e{int t,n;}; e es[maxn*2]; int ess,g[maxn]; 10 void pe(int f,int t){es[++ess]=(e){t,g[f]}; g[f]=ess; es[++ess]=(e){f,g[t]}; g[t]=ess;} 11 struct abc{int v,id;}; abc abcd[maxn]; bool cmp(abc a,abc b){return a.v<b.v;} 12 inline int read(){ 13 char ch=getchar(); int x=0,f=1; 14 while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1; ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘)x=x*10+ch-‘0‘,ch=getchar(); 15 return f*x; 16 } 17 void build1(int &x,int l,int r){ 18 x=++size; sz[x]=ch[x][0]=ch[x][1]=0; if(l==r)return; 19 int mid=(l+r)>>1; build1(ch[x][0],l,mid); build1(ch[x][1],mid+1,r); 20 } 21 void insert(int &x,int l,int r,int val){ 22 size++; sz[size]=sz[x]+1; ch[size][0]=ch[x][0]; ch[size][1]=ch[x][1]; x=size; if(l==r)return; 23 int mid=(l+r)>>1; if(val<=mid)insert(ch[x][0],l,mid,val); if(val>mid)insert(ch[x][1],mid+1,r,val); 24 } 25 void dfs(int x,int fa){ 26 rt[x]=rt[fa]; insert(rt[x],1,tot,v[x]); 27 for(int i=g[x];i;i=es[i].n)if(es[i].t!=fa){ 28 f[0][es[i].t]=x; dep[es[i].t]=dep[x]+1; dfs(es[i].t,x); 29 } 30 } 31 void build2(){ 32 for(int i=1;(1<<i)<=n;i++)inc(j,1,n)f[i][j]=f[i-1][f[i-1][j]]; 33 } 34 int LCA(int u,int v){ 35 if(dep[u]<dep[v])swap(u,v); int t=dep[u]-dep[v]; 36 for(int i=0;(1<<i)<=n;i++)if(t&(1<<i))u=f[i][u]; 37 for(int i=16;i>=0;i--)if(f[i][u]!=f[i][v])u=f[i][u],v=f[i][v]; 38 return u==v?u:f[0][u]; 39 } 40 int solve(int u,int v,int k){ 41 int lca=LCA(u,v),l=1,r=tot,x1=rt[v],x2=rt[f[0][lca]],x3=rt[u],x4=rt[lca]; 42 while(l<r){ 43 int mid=(l+r)>>1,tmp=sz[ch[x1][0]]-sz[ch[x2][0]]+sz[ch[x3][0]]-sz[ch[x4][0]]; 44 if(tmp>=k) 45 r=mid,x1=ch[x1][0],x2=ch[x2][0],x3=ch[x3][0],x4=ch[x4][0]; 46 else 47 k-=tmp,l=mid+1,x1=ch[x1][1],x2=ch[x2][1],x3=ch[x3][1],x4=ch[x4][1]; 48 } 49 return l; 50 } 51 int main(){ 52 n=read(); m=read(); inc(i,1,n)abcd[i]=(abc){read(),i}; inc(i,1,n-1){int a=read(),b=read(); pe(a,b);} 53 sort(abcd+1,abcd+1+n,cmp); 54 inc(i,1,n){if(i==1||abcd[i].v!=abcd[i-1].v)++tot,id[tot]=abcd[i].v; v[abcd[i].id]=tot;} 55 size=0; build1(rt[0],1,tot); dfs(1,0); build2(); 56 inc(i,1,m){ 57 int u=read()^last,v=read(),k=read(); last=id[solve(u,v,k)]; printf("%d",last); if(i!=m)puts(""); 58 } 59 return 0; 60 }
题解:用主席树维护某节点到根节点的权值数量sz,建树过程可以由父亲节点递推。询问就用倍增求出lca,然后路径上的sz值就为sz[u]-sz[lca]+sz[v]-sz[fa[lca]]。
反思:本弱倍增数组写反了,疯狂reQAQ~
20160623
5、bzoj3156 http://www.lydsy.com/JudgeOnline/problem.php?id=3156
题意:n个检查点,在第i个检查点放置塔花费a[i],放置木偶花费为该位置右边最近一个塔离它的距离。求最小花费。n≤1000000
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define ll long long 5 #define maxn 1000010 6 #define inc(i,j,k) for(int i=j;i<=k;i++) 7 using namespace std; 8 9 int n,q[maxn],l,r; ll c[maxn],f[maxn]; 10 inline int read(){ 11 char ch=getchar(); int x=0,f=1; 12 while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1; ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘)x=x*10+ch-‘0‘,ch=getchar(); 13 return x*f; 14 } 15 double calc(int j,int k){ 16 return (double)(2*f[j]-2*f[k]+j-k+(ll)j*j-(ll)k*k)/(double)(j-k); 17 } 18 int main(){ 19 n=read(); inc(i,1,n)c[i]=(ll)read(); l=r=0; 20 inc(i,1,n){ 21 while(l<r&&calc(q[l],q[l+1])<2*i)l++; f[i]=f[q[l]]+((ll)i-q[l])*(i-q[l]-1)/2+c[i]; 22 while(l<r&&calc(q[r-1],q[r])>calc(q[r],i))r--; q[++r]=i; 23 } 24 printf("%lld",f[n]); 25 }
题解:从右往左处理。在第i个点放塔的费用f[i]=min{f[j]+sigma(k,i+1,j-1)k-i}+a[i],用等差数列求和公式化简后作斜率dp,具体看代码。
反思:本弱公式总是推错,要稳!
6、bzoj3673 http://www.lydsy.com/JudgeOnline/problem.php?id=3673
题意:维护可以恢复到第k次操作后的并查集。点数,操作数≤20000
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define maxn 30000 5 #define inc(i,j,k) for(int i=j;i<=k;i++) 6 using namespace std; 7 8 int fa[maxn*15],ch[maxn*15][2],dep[maxn*15],pos[maxn*15],sz,n,m,rt[maxn]; 9 inline int read(){ 10 char ch=getchar(); int f=1,x=0; 11 while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1; ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘)x=x*10+ch-‘0‘,ch=getchar(); 12 return f*x; 13 } 14 void build(int &x,int l,int r){ 15 x=++sz; if(l==r){fa[x]=l; dep[x]=1; pos[x]=l; return;} 16 int mid=(l+r)>>1; build(ch[x][0],l,mid); build(ch[x][1],mid+1,r); 17 } 18 void updatefa(int &x,int l,int r,int a,int b){ 19 sz++; fa[sz]=fa[x]; dep[sz]=dep[x]; pos[sz]=pos[x]; ch[sz][0]=ch[x][0]; ch[sz][1]=ch[x][1]; 20 x=sz; if(l==r){fa[x]=b; return;} 21 int mid=(l+r)>>1; if(a<=mid)updatefa(ch[x][0],l,mid,a,b);else updatefa(ch[x][1],mid+1,r,a,b); 22 } 23 void updatedep(int &x,int l,int r,int a,int b){ 24 sz++; fa[sz]=fa[x]; dep[sz]=dep[x]; pos[sz]=pos[x]; ch[sz][0]=ch[x][0]; ch[sz][1]=ch[x][1]; 25 x=sz; if(l==r){dep[x]=b; return;} 26 int mid=(l+r)>>1; if(a<=mid)updatedep(ch[x][0],l,mid,a,b);else updatedep(ch[x][1],mid+1,r,a,b); 27 } 28 int query(int x,int l,int r,int a){ 29 if(l==r)return x; int mid=(l+r)>>1; 30 if(a<=mid)return query(ch[x][0],l,mid,a);else return query(ch[x][1],mid+1,r,a); 31 } 32 int find(int x,int y){ 33 int z=query(x,1,n,y); if(fa[z]==pos[z])return z;else return find(x,fa[z]); 34 } 35 void merge(int &s,int x,int y){ 36 int z1=find(s,x),z2=find(s,y); if(pos[z1]==pos[z2])return; if(dep[z1]>dep[z2])swap(z1,z2); 37 int abc=max(dep[z2],dep[z1]+1); updatefa(s,1,n,pos[z1],pos[z2]); updatedep(s,1,n,pos[z2],abc); 38 } 39 int main(){ 40 n=read(); m=read(); build(rt[0],1,n); 41 inc(i,1,m){ 42 int opt=read(); 43 if(opt==1){int a=read(),b=read(); rt[i]=rt[i-1]; merge(rt[i],a,b);} 44 if(opt==2){int k=read(); rt[i]=rt[k];} 45 if(opt==3){ 46 int a=read(),b=read(); rt[i]=rt[i-1]; 47 if(pos[find(rt[i],a)]==pos[find(rt[i],b)])puts("1");else puts("0"); 48 } 49 } 50 return 0; 51 }
题解:用可持久化线段树维护并查集的fa数组和秩(在并查集里的深度),不能路径压缩所以用按秩启发式合并,可以使合并均摊复杂度为O(nlog2n)。
可持久化线段树实际上就是在更新节点时按主席树的插入方式新建一条路径(其实主席树就是可持久化权值线段树)。
7、bzoj3674 http://www.lydsy.com/JudgeOnline/problem.php?id=3673
题意:同3673,但强制在线且点数操作数≤200000
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define maxn 200010 5 #define inc(i,j,k) for(int i=j;i<=k;i++) 6 using namespace std; 7 8 int fa[maxn*50],ch[maxn*50][2],dep[maxn*50],pos[maxn*50],sz,n,m,rt[maxn]; 9 inline int read(){ 10 char ch=getchar(); int f=1,x=0; 11 while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1; ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘)x=x*10+ch-‘0‘,ch=getchar(); 12 return f*x; 13 } 14 void build(int &x,int l,int r){ 15 x=++sz; if(l==r){fa[x]=l; dep[x]=1; pos[x]=l; return;} 16 int mid=(l+r)>>1; build(ch[x][0],l,mid); build(ch[x][1],mid+1,r); 17 } 18 void updatefa(int &x,int l,int r,int a,int b){ 19 sz++; fa[sz]=fa[x]; dep[sz]=dep[x]; pos[sz]=pos[x]; ch[sz][0]=ch[x][0]; ch[sz][1]=ch[x][1]; 20 x=sz; if(l==r){fa[x]=b; return;} 21 int mid=(l+r)>>1; if(a<=mid)updatefa(ch[x][0],l,mid,a,b);else updatefa(ch[x][1],mid+1,r,a,b); 22 } 23 void updatedep(int &x,int l,int r,int a,int b){ 24 sz++; fa[sz]=fa[x]; dep[sz]=dep[x]; pos[sz]=pos[x]; ch[sz][0]=ch[x][0]; ch[sz][1]=ch[x][1]; 25 x=sz; if(l==r){dep[x]=b; return;} 26 int mid=(l+r)>>1; if(a<=mid)updatedep(ch[x][0],l,mid,a,b);else updatedep(ch[x][1],mid+1,r,a,b); 27 } 28 int query(int x,int l,int r,int a){ 29 if(l==r)return x; int mid=(l+r)>>1; 30 if(a<=mid)return query(ch[x][0],l,mid,a);else return query(ch[x][1],mid+1,r,a); 31 } 32 int find(int x,int y){ 33 int z=query(x,1,n,y); if(fa[z]==pos[z])return z;else return find(x,fa[z]); 34 }
以上是关于20160619~20160625 8的主要内容,如果未能解决你的问题,请参考以下文章