码迷,mamicode.com
首页 > 其他好文 > 详细

20160619~20160625 8

时间:2016-07-03 23:23:10      阅读:152      评论:0      收藏:0      [点我收藏+]

标签:

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 }
View Code

题解:先将每个单词插入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 }
View Code

题解: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 }
View Code

题解:在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 }
View Code

题解:用主席树维护某节点到根节点的权值数量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 }
View Code

题解:从右往左处理。在第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 }
View Code

题解:用可持久化线段树维护并查集的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 }
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); int last=0;
41     inc(i,1,m){
42         int opt=read();
43         if(opt==1){int a=read()^last,b=read()^last; rt[i]=rt[i-1]; merge(rt[i],a,b);}
44         if(opt==2){int k=read()^last; rt[i]=rt[k];}
45         if(opt==3){
46             int a=read()^last,b=read()^last; rt[i]=rt[i-1];
47             if(pos[find(rt[i],a)]==pos[find(rt[i],b)])puts("1"),last=1;else puts("0"),last=0;
48         }
49     }
50     return 0;
51 }
View Code

题解:T个不停,后来看黄学长博客把数组n*2(log2n)开成结果A了,后来突然明白我fa数组和dep数组是分开维护的,也就是说每次操作新建了两条路径,当然要*2,QAQ~

8、bzoj2563 http://www.lydsy.com/JudgeOnline/problem.php?id=2563

题意:一个n(偶数)点图,节点权值为w(v),边权为c(e)。两人轮流将图中的顶点染色,已经被染过色的点不能再染了,而且每一轮都必须给一个且仅一个顶点染色。染完后每个人的分数为染过的点权和以及两个端点都被染的边权和。如果两人都是采用最优策略的,求最终第一个人的分数减去第二个人的分数。n≤10000,边数≤100000

代码:

技术分享
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #define maxn 10010
 5 #define inc(i,j,k) for(int i=j;i<=k;i++)
 6 #define ll long long
 7 using namespace std;
 8 
 9 int n,m; ll w[maxn],ans1,ans2;
10 bool cmp(ll a,ll b){return a>b;}
11 inline int read(){
12     char ch=getchar(); int f=1,x=0;
13     while(ch<0||ch>9){if(ch==-)f=-1; ch=getchar();} while(ch>=0&&ch<=9)x=x*10+ch-0,ch=getchar();
14     return f*x;
15 }
16 int main(){
17     n=read(); m=read(); inc(i,1,n)w[i]=(ll)read()<<1,ans2+=w[i]>>1;
18     inc(i,1,m){int a=read(),b=read(); ll c=(ll)read(); ans2+=c; w[a]+=c; w[b]+=c;} sort(w+1,w+n+1,cmp);
19     inc(i,1,n){if(i&1)ans1+=w[i];}
20     printf("%lld",ans1-ans2); return 0;
21 }
View Code

题解:本弱只能引用神犇的题解

考虑先手选择每个点对答案的影响

一个点如果不选,本身对答案的贡献是-w,一个点如果选,本身对答案的贡献是w,一条边如果两个端点都不选,对答案的贡献是-c,如果两个端点中只选择一个,对答案的贡献是0,如果两个端点都选,对答案的贡献是c,那么我们先预先把所有的权值都在初始答案中减掉,然后就变成了:

一个点如果不选,本身对答案的贡献是0,一个点如果选,本身对答案的贡献是2*w,一条边如果两个端点都不选,对答案的贡献是0,如果两个端点中只选择一个,对答案的贡献是c,如果两个端点都选,对答案的贡献是2*c,那么令一个点的贡献值为本身点权的二倍+所有相连的边的边权,排个序两人轮流取最大即可。

20160619~20160625 8

标签:

原文地址:http://www.cnblogs.com/YuanZiming/p/5616753.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!