2018.8.7 Noip2018模拟测试赛(二十)
日期: |
八月七号 |
总分: |
300分 |
难度: |
提高 ~ 省选 |
得分: |
100分(呵呵一笑) |
题目列表:
T1:SS
T2:Tree Game
T3:二元运算
赛后反思:
Emmmmmm……
开局随便看,第二题发现手算样例不对……
比赛快结束时,又看了看题,才发现自己看错了……
最终,改A了……Emmmmmm……
题解:
T1:SS
特别特别玄学的一道题……
原串是一个偶串,设半个原串为S,加起来就是SS。
设S的最长公共前缀后缀为T,(用 KMP 的 next 求)
那么接下来的串为 STST,STSSTS,STSSTSTSST……
只看一半:S,ST,STS,STSST……
这不是斐波那契吗?由于斐波那增长很快,我们暴力求出在 l,r 的串,差分出答案。
CODE:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 6 char s[500005]; 7 long long l,r,siz[500005],sum[500005][26],ans[26]; 8 int n,lim,nxt[500005]; 9 10 void get_next(){ 11 int i=1,j=0; 12 nxt[1]=0; 13 while(i<=n){ 14 if(j==0||s[i]==s[j]){ 15 nxt[++i]=++j; 16 }else j=nxt[j]; 17 } 18 } 19 20 void get_ans(long long x,int f){ 21 for(int i=lim;i>=0;i--) 22 if(x>=siz[i]){ 23 for(int j=0;j<26;j++) 24 ans[j]+=f*sum[i][j]; 25 x-=siz[i]; 26 } 27 for(int i=1;i<=x;i++)ans[s[i]-'a']+=f; 28 } 29 30 int main(){ 31 scanf("%s%lld%lld",s+1,&l,&r); 32 n=strlen(s+1)>>1; 33 get_next(); 34 int t=nxt[n]; 35 for(int i=1;i<=n;i++)sum[0][s[i]-'a']++; 36 for(int i=0;i<26;i++)sum[1][i]=sum[0][i]; 37 for(int i=1;i<=n-t;i++)sum[1][s[i]-'a']++; 38 siz[0]=n,siz[1]=2*n-t; 39 for(int i=2;i<=100;i++){ 40 for(int j=0;j<26;j++) 41 sum[i][j]=sum[i-1][j]+sum[i-2][j]; 42 siz[i]=siz[i-1]+siz[i-2]; 43 if(siz[i]>1e18){lim=i;break;} 44 } 45 get_ans(l-1,-1); 46 get_ans(r,1); 47 for(int i=0;i<26;i++)printf("%lld ",ans[i]); 48 return 0; 49 }
T2:Tree Game
你可以很轻易的发现一个结论,先手必须要往权值更小的点走,不然对手跟你对着干就能把你逼死。
当前子树如果有必胜的策略,那就一定有一个儿子的子树是必胜的,且权值小于它。
于是乎,dp秒杀吧!时间 $O(n^2)$
CODE:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 6 int tot=0,h[3005]; 7 int n,x,y,f[3005],val[3005]; 8 struct Edge{ 9 int x,next; 10 }e[6005]; 11 12 inline void add_edge(int x,int y){ 13 e[++tot].x=y; 14 e[tot].next=h[x],h[x]=tot; 15 } 16 17 void dfs(int x){ 18 f[x]=0; 19 for(int i=h[x];i;i=e[i].next){ 20 if(~f[e[i].x])continue; 21 dfs(e[i].x); 22 if(val[e[i].x]<val[x]&&f[e[i].x]==0)f[x]=1; 23 } 24 } 25 26 int main(){ 27 scanf("%d",&n); 28 for(int i=1;i<=n;i++)scanf("%d",val+i); 29 for(int i=1;i<n;i++){ 30 scanf("%d%d",&x,&y); 31 add_edge(x,y); 32 add_edge(y,x); 33 } 34 for(int i=1;i<=n;i++){ 35 memset(f,-1,sizeof(f)); 36 dfs(i); 37 if(f[i])printf("%d ",i); 38 } 39 }
T3:二元运算
CDQ分治加FFT。
如果只有第一种运算就是裸的FFT求卷积,只有第二种运算可以把B序列翻转,然后求卷积即可。
但是有 x 与 y 大小关系的限制使得我们不能直接求卷积来得出答案。
考虑分治,对于每个区间 $[l,r]$,处理出$A$中的$[l,mid]$与$B$中的$[mid+1,r]$对答案的贡献以及$A$中的$[mid+1,r]$与$B$中的$[l,mid]$对答案的贡献,这两个是有严格的 x 与 y 的大小关系的,分别使用 FFT 求卷积解决。再递归处理子区间即可。
时间复杂度$O(Tnlog^2n)$
CODE:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 using namespace std; 6 7 #pragma GCC optimize(3) 8 9 const double PI=acos(-1); 10 int T,n,m,q,x; 11 int rev[400005],A[200005],B[200005]; 12 long long ans[400005]; 13 struct cmplx{ 14 double x,y; 15 cmplx(double a=0,double b=0){x=a;y=b;} 16 cmplx operator+(cmplx b){ 17 return cmplx(x+b.x,y+b.y); 18 } 19 cmplx operator+=(cmplx b){ 20 return *this=*this+b; 21 } 22 cmplx operator-(cmplx b){ 23 return cmplx(x-b.x,y-b.y); 24 } 25 cmplx operator*(cmplx b){ 26 return cmplx(x*b.x-y*b.y,x*b.y+y*b.x); 27 } 28 cmplx operator*=(cmplx b){ 29 return *this=*this*b; 30 } 31 }a[400005],b[400005]; 32 33 int read(){ 34 int x=0; 35 char c; 36 do c=getchar(); 37 while(!isdigit(c)); 38 do{ 39 x=(x<<1)+(x<<3)+(c^48); 40 c=getchar(); 41 }while(isdigit(c)); 42 return x; 43 } 44 45 void FFT(cmplx a[],int bit,int dft){ 46 for(int i=0;i<bit;i++) 47 if(i<rev[i])swap(a[i],a[rev[i]]); 48 for(int i=1;i<bit;i<<=1){ 49 cmplx W(cos(PI/i),dft*sin(PI/i)); 50 for(int j=0;j<bit;j+=i<<1){ 51 cmplx w(1,0); 52 for(int k=j;k<i+j;k++,w=w*W){ 53 cmplx x=a[k]; 54 cmplx y=w*a[k+i]; 55 a[k]=x+y,a[k+i]=x-y; 56 } 57 } 58 } 59 if(dft==-1)for(int i=0;i<bit;i++)a[i].x/=bit; 60 } 61 62 void solve(int l,int r){ 63 if(l==r)return; 64 int mid=l+r>>1,n=1; 65 while(n<=r-l+1)n<<=1; 66 for(int i=l;i<=mid;i++) 67 a[i-l]=cmplx(A[i],0); 68 for(int i=mid+1;i<=r;i++) 69 b[i-mid-1]=cmplx(B[i],0); 70 for(int i=mid-l+1;i<n;i++) 71 a[i]=cmplx(0,0); 72 for(int i=r-mid;i<n;i++) 73 b[i]=cmplx(0,0); 74 for(int i=0;i<n;i++) 75 rev[i]=(rev[i>>1]>>1)|(i&1)*(n>>1); 76 FFT(a,n,1),FFT(b,n,1); 77 for(int i=0;i<n;i++)a[i]=a[i]*b[i]; 78 FFT(a,n,-1); 79 for(int i=0;i<=r-l+1;i++) 80 ans[i+l+mid+1]+=(long long)(a[i].x+0.5); 81 solve(l,mid),solve(mid+1,r); 82 } 83 84 int main(){ 85 T=read(); 86 while(T--){ 87 memset(A,0,sizeof(A)); 88 memset(B,0,sizeof(B)); 89 memset(a,0,sizeof(a)); 90 memset(b,0,sizeof(b)); 91 memset(ans,0,sizeof(ans)); 92 int maxn=0,bit=1; 93 n=read(),m=read(),q=read(); 94 for(int i=1;i<=n;i++){ 95 x=read(); 96 A[x]++,maxn=max(maxn,x); 97 } 98 for(int i=1;i<=m;i++){ 99 x=read(); 100 B[x]++,maxn=max(maxn,x); 101 } 102 while(bit<=maxn<<1)bit<<=1; 103 for(int i=0;i<bit;i++) 104 rev[i]=(rev[i>>1]>>1)|(i&1)*(bit>>1); 105 for(int i=0;i<=maxn;i++) 106 b[maxn-i].x=B[i]; 107 for(int i=0;i<=maxn;i++) 108 a[i].x=A[i]; 109 FFT(a,bit,1),FFT(b,bit,1); 110 for(int i=0;i<bit;i++)a[i]=a[i]*b[i]; 111 FFT(a,bit,-1); 112 for(int i=0;i<=maxn;i++) 113 ans[i]=(long long)(a[i+maxn].x+0.5); 114 solve(0,maxn); 115 for(int i=1;i<=q;i++){ 116 x=read(); 117 printf("%lld\n",ans[x]); 118 } 119 } 120 }