轉載請註明原文地址http://www.cnblogs.com/LadyLex/p/8536399.html html
據說今年省選很可怕?刷題刷題刷題node
省選已經結束了可是咱們要繼續刷題刷題刷題數組
目標是「有思惟難度的DP題」!數據結構
這個不用多說……NOI2017的D1T3,難度確定是有的ide
我的以爲那個dp方程難想……函數
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 using namespace std; 5 #define mod 998244353 6 #define K 1010 7 #define L 2048 8 #define RG register 9 #define LL long long 10 inline int quick_mod(int di,int mi) 11 { 12 int ret=1; 13 for(;mi;mi>>=1,di=(LL)di*di%mod) 14 if(mi&1)ret=(LL)ret*di%mod; 15 return ret; 16 } 17 int poww[L],logi[L],rev[L],len=1; 18 inline void dft(int *a,int ra,int opt) 19 { 20 register int i,j,l,d=logi[len]-logi[ra],wn,tmp,*w,*x,*y; 21 for(i=0;i<ra;++i)if(i<(rev[i]>>d))swap(a[i],a[rev[i]>>d]); 22 for(d=2;d<=ra;d<<=1) 23 for(wn=((opt==1)?(len/d):(-len/d)),i=0,l=(d>>1);i<ra;i+=d) 24 for(w=poww+(opt==1?0:len),j=0,x=a+i,y=x+l;j<l;++j,++x,++y,w+=wn) 25 tmp=(LL)(*w)*(*y)%mod,*y=(*x-tmp+mod)%mod,*x=(*x+tmp)%mod; 26 if(opt==-1) 27 for(tmp=quick_mod(ra,mod-2),i=0;i<ra;++i)a[i]=(LL)a[i]*tmp%mod; 28 } 29 int n,k,p,q,pp[K]; 30 int g[K][K],h[K][K],f[K<<1]; 31 inline int min(int a,int b){return a<b?a:b;} 32 int tmp1[L]; 33 inline int get_inv(int *a,int *ret,int ra) 34 { 35 if(ra==1){ret[0]=quick_mod(a[0],mod-2);return 1;} 36 RG int i,la=1,r1=get_inv(a,ret,ra+1>>1); 37 while(la<(ra<<1))la<<=1; 38 memcpy(tmp1,a,ra<<2),memset(tmp1,0,(la-ra)<<2); 39 memset(ret,0,(la-r1)<<2); 40 dft(tmp1,la,1),dft(ret,la,1); 41 for(i=0;i<la;++i)ret[i]=(LL)ret[i]*(2+mod-(LL)tmp1[i]*ret[i]%mod)%mod; 42 dft(ret,la,-1);return ra; 43 } 44 inline void rev_copy(int *to,int *st,int ra) 45 {for(RG int i=0;i<ra;++i,++to)*to=st[ra-i-1];} 46 inline void reverse(int *st,int ra) 47 {for(RG int t,i=0,j=ra-1;i<j;++i,--j)t=st[i],st[i]=st[j],st[j]=t;} 48 int tmp2[L],tmp3[L]; 49 inline int get_mod(int *a,int ra,int *p,int rp,int *ret) 50 { 51 while(ra&&!a[ra-1])--ra; 52 while(rp&&!p[rp-1])--rp; 53 if(ra<rp){memcpy(ret,a,ra<<2),memset(ret+ra,0,(rp-ra)<<2);return rp;} 54 RG int i,j,re=ra-rp+1,la=1; 55 while(la<(re<<1))la<<=1; 56 rev_copy(tmp2,p,rp);memset(tmp2+re,0,(la-re)<<2); 57 get_inv(tmp2,tmp3,re),memset(tmp3+re,0,(la-re)<<2); 58 rev_copy(tmp2,a,ra),memset(tmp2+re,0,(la-re)<<2); 59 dft(tmp3,la,1);dft(tmp2,la,1); 60 for(i=0;i<la;++i)tmp2[i]=(LL)tmp2[i]*tmp3[i]%mod; 61 dft(tmp2,la,-1); 62 la=1;while(la<ra)la<<=1; 63 reverse(tmp2,re), 64 memset(tmp2+re,0,(la-re)<<2); 65 memcpy(tmp3,p,rp<<2),memset(tmp3+rp,0,(la-rp)<<2); 66 dft(tmp2,la,1),dft(tmp3,la,1); 67 for(i=0;i<la;++i)tmp2[i]=(LL)tmp2[i]*tmp3[i]%mod; 68 dft(tmp2,la,-1); 69 for(i=0;i<rp;++i)ret[i]=(a[i]-tmp2[i]+mod)%mod; 70 memset(ret+rp,0,(la-rp)<<2); 71 while(rp&&!ret[rp-1])--rp; 72 return rp; 73 } 74 int c[L],d[L],e[L],tmp[L]; 75 inline int calc(int k) 76 { 77 RG int i,j,u,ra=k+1,r1=k+2,lim,la=1; 78 memset(g,0,sizeof(g)); 79 memset(h,0,sizeof(h)); 80 g[k][1]=(LL)pp[k]*q%mod; 81 g[k][0]=h[k][0]=1; 82 for(i=k-1;i>0;--i) 83 { 84 ra=k/i,g[i][0]=h[i][0]=1; 85 for(j=1;j<=ra;++j) 86 for(u=0;u<j;++u) 87 h[i][j]=(h[i][j]+(LL)h[i][u]*g[i+1][j-u-1]%mod*pp[i]%mod*q)%mod; 88 for(j=1;j<=ra;++j) 89 for(u=0;u<=j;++u) 90 g[i][j]=(g[i][j]+(LL)h[i][u]*g[i+1][j-u])%mod; 91 } 92 memset(f,0,sizeof(f)),f[0]=1; 93 for(i=1;i<=(ra<<1);++i) 94 for(j=1,lim=min(k+1,i);j<=lim;++j) 95 f[i]=(f[i]+(LL)f[i-j]*g[1][j-1]%mod*q)%mod; 96 int ret=0; 97 if(n<=(ra<<1)) 98 { 99 for(i=max(0,n-k);i<=n;++i) 100 ret=(ret+(LL)f[i]*g[1][n-i])%mod; 101 return ret; 102 } 103 memset(c,0,sizeof(c)),c[1]=1; 104 memset(d,0,sizeof(d));d[ra]=1; 105 for(i=1;i<=ra;++i)d[ra-i]=(LL)q*g[1][i-1]%mod; 106 memset(e,0,sizeof(e)),e[0]=1; 107 while(la<(ra<<1))la<<=1; 108 for(lim=n-k-1;lim;lim>>=1) 109 { 110 if(lim&1) 111 { 112 memcpy(tmp,c,k<<2);memset(tmp+k,0,(la-k)<<2); 113 dft(e,la,1),dft(tmp,la,1); 114 for(i=0;i<la;++i)e[i]=(LL)e[i]*tmp[i]%mod; 115 dft(e,la,-1); 116 get_mod(e,k<<1,d,ra,e); 117 } 118 dft(c,la,1); 119 for(i=0;i<la;++i)c[i]=(LL)c[i]*c[i]%mod; 120 dft(c,la,-1); 121 get_mod(c,k<<1,d,ra,c); 122 } 123 124 } 125 int main() 126 { 127 RG int i,x,y; 128 scanf("%d%d%d%d",&n,&k,&x,&y);logi[1]=0; 129 while(len<=(k+1<<1))len<<=1,logi[len]=logi[len>>1]+1; 130 poww[0]=poww[len]=1,poww[1]=quick_mod(3,(mod-1)/len); 131 for(i=2;i<len;++i)poww[i]=(LL)poww[i-1]*poww[1]%mod; 132 for(i=0;i<len;++i) 133 if(i&1)rev[i]=(rev[i>>1]>>1)|(len>>1); 134 else rev[i]=(rev[i>>1]>>1); 135 p=(LL)x*quick_mod(y,mod-2)%mod,q=(LL)(y-x)*quick_mod(y,mod-2)%mod; 136 for(pp[0]=1,pp[1]=p,i=2;i<=k;++i)pp[i]=(LL)pp[i-1]*p%mod; 137 printf("%d\n",(calc(k)-calc(k-1)+mod)%mod); 138 }
題目模型不算特別新的數位DP,能想到它在幹什麼,可是……優化
那個式子,想推對必須很是嚴謹才行……ui
1 #include <cstdio> 2 #include <cstring> 3 using namespace std; 4 #define RG register 5 #define mod 20130427 6 #define N 100010 7 #define LL long long 8 char cB[1<<15],*S=cB,*T=cB; 9 #define getc (S==T&&(T=(S=cB)+fread(cB,1,1<<15,stdin),S==T)?0:*S++) 10 inline int read() 11 { 12 RG int x=0;RG char c=getc; 13 while(c<'0'|c>'9')c=getc; 14 while(c>='0'&c<='9')x=10*x+(c^48),c=getc; 15 return x; 16 } 17 inline int Sum(int a){return ((LL)a*(a+1ll)/2)%mod;} 18 inline int max(int a,int b){return a>b?a:b;} 19 inline int min(int a,int b){return a<b?a:b;} 20 int ge2[N],sum2[N],ge3[N],sum3[N],no_zero_sum3[N],B,lena,bit[N],lenb,bin[N]; 21 inline int calc(bool start,int left,int lim,int cnt,int sum,int sum_of_substring ) 22 { 23 if(lim==0)return 0; 24 if(start) 25 return 26 ( 27 (LL) Sum(lim-1) * ge2[left] %mod * bin[left] %mod + //前與後鏈接以後前面的貢獻 28 (LL) ( lim - 1 ) %mod * sum3[left] %mod + no_zero_sum3[left] %mod +//後面的子串 29 (LL) ( lim - 1 ) %mod * sum2[left] %mod//前與後鏈接以後後面的貢獻 30 )%mod; 31 int new_sum=( (LL) sum * B %mod * lim %mod + (LL) Sum(lim-1) * cnt %mod ) %mod; 32 return 33 ( 34 (LL) sum_of_substring * lim %mod * bin[left] %mod + //以前的子串 35 (LL) new_sum * ge2[left] %mod * bin[left] %mod + //前與後鏈接以後前面的貢獻 36 (LL) lim %mod * sum3[left] %mod +//後面的子串 不乘cnt 37 (LL) cnt * lim %mod * sum2[left] %mod//前與後鏈接以後後面的貢獻 ,要乘cnt 由於再前面不一樣 38 )%mod; 39 } 40 inline int dfs(bool start,int st,int cnt,int sum,int sum_of_substring) 41 { 42 if(st==0)return 0; 43 int new_sum=( (LL) sum * B %mod + (LL) ( cnt + 1 ) * bit[st] %mod )%mod; 44 return 45 ( new_sum + calc(start, st-1 , bit[st] , cnt + 1 , sum , sum_of_substring ) + 46 dfs(0, st - 1 , cnt + 1, new_sum , (sum_of_substring + new_sum)%mod ) )%mod; 47 } 48 signed main() 49 { 50 RG int i,j,len,ans; 51 B=read(),lena=read(); 52 for(i=lena;i;--i)bit[i]=read(); 53 lenb=read();len=max(lena,lenb); 54 if(lena>1||bit[1]>0) 55 { 56 --bit[1],j=1; 57 while(bit[j]<0)bit[j]+=B,--bit[j+1],++j; 58 while(lena>1&&!bit[lena])--lena; 59 } 60 for(bin[0]=ge2[0]=i=1;i<=len;++i) 61 { 62 bin[i]=(LL)bin[i-1]*B%mod; 63 ge2[i]=( ge2[i-1] + bin[i] )%mod; 64 ge3[i]=(LL) Sum(i) * bin[i] %mod; 65 sum2[i]=( (LL) sum2[i-1] * B %mod + Sum ( bin[i] - 1 ) )%mod; 66 sum3[i]=( (LL) Sum(B-1) * bin[i-1] %mod * ge2[i-1] %mod + (LL)B * sum2[i-1] %mod + (LL) B * sum3[i-1] %mod )%mod; 67 no_zero_sum3[i]=( (LL) Sum(B-1) * bin[i-1] %mod * ge2[i-1] %mod + 68 (LL) (B - 1) * sum2[i-1] %mod + (LL) (B - 1) * sum3[i-1] %mod + no_zero_sum3[i-1] )%mod; 69 } 70 ans=mod-dfs(1,lena,0,0,0); 71 for(i=lenb;i;--i)bit[i]=read(); 72 printf("%d\n",(ans+dfs(1,lenb,0,0,0))%mod); 73 }
二進制的數位DPspa
聽dalao們說挺簡單……可是我以爲是比較好的一道數位dp設計
wq的作法又簡潔又快,可是我太弱了不會……
只好打一個稍微麻煩的dp了
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 using namespace std; 5 #define RG register 6 #define LL long long 7 LL n,m,k,bin[64],f_ge[64][2][2][2],f_sum[64][2][2][2]; 8 int main() 9 { 10 RG int mod,i,j,ta,tb,tc,a,b,c,x,y,z,t,len,lena,lenb,lenc,bita,bitb,bitc;LL ans,tmp; 11 scanf("%d",&t); 12 while(t--) 13 { 14 scanf("%lld%lld%lld%d",&n,&m,&k,&mod),--n,--m; 15 lena=lenb=lenc=0; 16 tmp=n;while(tmp)++lena,tmp>>=1; 17 tmp=m;while(tmp)++lenb,tmp>>=1; 18 tmp=k;while(tmp)++lenc,tmp>>=1; 19 len=max(lena,max(lenb,lenc)); 20 for(bin[0]=i=1;i<=len;++i)bin[i]=(bin[i-1]<<1)%mod; 21 memset(f_ge,0,sizeof(f_ge)),memset(f_sum,0,sizeof(f_sum)); 22 f_ge[len+1][1][1][1]=1;ans=0; 23 for(i=len;~i;--i) 24 { 25 bita=(n>>i)&1,bitb=(m>>i)&1,bitc=(k>>i)&1; 26 for(a=0;a<2;++a)for(b=0;b<2;++b)for(c=0;c<2;++c) 27 if(f_ge[i+1][a][b][c]) 28 for(x=0;x<2;++x) 29 { 30 if(a && x>bita)break; 31 for(y=0;y<2;++y) 32 { 33 if(b && y>bitb)break; 34 z=x^y; 35 if(c && z<bitc)continue; 36 ta=(a && bita==x)?1:0,tb=(b && bitb==y)?1:0,tc=(c && bitc==z)?1:0; 37 f_ge[i][ta][tb][tc]=(f_ge[i][ta][tb][tc]+f_ge[i+1][a][b][c])%mod; 38 f_sum[i][ta][tb][tc]=( f_sum[i][ta][tb][tc]+f_sum[i+1][a][b][c])%mod; 39 if(z)f_sum[i][ta][tb][tc]=( f_sum[i][ta][tb][tc]+ bin[i]*f_ge[i+1][a][b][c]%mod )%mod; 40 } 41 } 42 } 43 k%=mod; 44 for(a=0;a<2;++a)for(b=0;b<2;++b)for(c=0;c<2;++c) 45 ans=(ans+f_sum[0][a][b][c]-k*f_ge[0][a][b][c]%mod+mod)%mod; 46 printf("%lld\n",ans); 47 } 48 }
很棒的插頭(輪廓線)dp題目……我從一開始就沒有想出來……
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 #define LL long long 6 #define RG register 7 #define N 15 8 #define mod 998244353 9 #define D 51 10 #define SZ 612497 11 int n,m,ans[110]; 12 LL K,B[N][N],lower,bin[15]; 13 char row[N],line[N]; 14 struct node{LL state;int next,cnt,val;}; 15 struct hash_map 16 { 17 node s[SZ+10];int e,adj[SZ+10]; 18 inline void init(){e=0;memset(adj,0,sizeof(adj));} 19 inline void update(LL state,int val,int cnt) 20 { 21 RG int i,pos=(state%SZ+(LL)val*SZ)%SZ; 22 for(i=adj[pos];i&&(s[i].state!=state||s[i].val!=val);i=s[i].next); 23 if(!i)s[++e].state=state,s[e].val=val,s[e].cnt=cnt,s[e].next=adj[pos],adj[pos]=e; 24 else s[i].cnt=(s[i].cnt+cnt)%mod; 25 } 26 }f[2]; 27 inline void execute(int x,int y,int cur) 28 { 29 RG int i,j,last=cur^1,tot=f[last].e,val,val1,val2,cnt,sum,half; 30 LL state,nstate; 31 f[cur].init(); 32 for(i=1;i<=tot;++i) 33 { 34 state=f[last].s[i].state,val=f[last].s[i].val,cnt=f[last].s[i].cnt; 35 nstate=state%bin[y-1],state/=bin[y-1]; 36 val1=state%D,state/=D; 37 val2=state%D,state/=D; 38 sum=val1+val2+B[x][y],half=sum>>1; 39 f[cur].update(nstate+state*bin[y+1]+(sum-half)*bin[y]+half*bin[y-1],val,cnt); 40 f[cur].update(nstate+state*bin[y+1]+half*bin[y]+(sum-half)*bin[y-1],val,cnt); 41 } 42 } 43 int main() 44 { 45 RG int i,j,cur=0; 46 for(bin[0]=i=1;i<=11;++i)bin[i]=bin[i-1]*D; 47 scanf("%d%d%lld",&n,&m,&K); 48 scanf("%s",row+1),scanf("%s",line+1); 49 B[1][1]=K; 50 for(i=1;i<=n;++i) 51 for(j=1;j<=m;++j) 52 B[i+1][j]+=B[i][j]>>1,B[i][j+1]+=B[i][j]>>1,B[i][j]&=1; 53 for(i=1;i<=n;++i)if(row[i]=='1')lower+=B[i][m+1]; 54 for(j=1;j<=m;++j)if(line[j]=='1')lower+=B[n+1][j]; 55 f[0].update(0,0,1); 56 RG int tot,last,val,cnt;LL state; 57 for(i=1;i<=n;++i) 58 { 59 for(j=1;j<=m;++j)cur^=1,execute(i,j,cur); 60 cur^=1,last=cur^1;tot=f[last].e; 61 f[cur].init(); 62 for(j=1;j<=tot;++j) 63 { 64 state=f[last].s[j].state,val=f[last].s[j].val; 65 if(row[i]=='1')val+=state/bin[m]; 66 state=(state%bin[m])*D; 67 f[cur].update(state,val,f[last].s[j].cnt); 68 } 69 } 70 for(i=1;i<=f[cur].e;++i) 71 { 72 state=f[cur].s[i].state/D; 73 val=f[cur].s[i].val; 74 for(j=1;j<=m;++j,state/=D)if(line[j]=='1')val+=state%D; 75 ans[val]=(ans[val]+f[cur].s[i].cnt)%mod; 76 } 77 for(i=1;i<=n*m;++i)ans[i]=(ans[i]+ans[i-1])%mod; 78 RG int q;LL l,r; 79 scanf("%d",&q); 80 while(q--) 81 { 82 scanf("%lld%lld",&l,&r); 83 if(r<lower||l>lower+n*m){puts("0");continue;} 84 l=max(0ll,l-lower),r=min((LL)n*m,r-lower); 85 printf("%d\n",l?(ans[r]-ans[l-1]+mod)%mod:ans[r]); 86 } 87 }
NOI2015D1T3,難度仍是稍微有的
我本身只想出了50pts作法,其實只差一點,把n/2改爲$\sqrt(n)$的複雜度就好了
1 #include <cstdio> 2 #include <cstring> 3 #include <vector> 4 using namespace std; 5 inline int min(int a,int b){return a<b?a:b;} 6 #define RG register 7 #define LL long long 8 #define N 510 9 #define L 266 10 int f[L][L],g[2][L][L]; 11 vector<int>cont[N]; 12 int n,mod,ans,lim,full,prime[N],id[N],tot;bool vis[N]; 13 int bin[10]; 14 inline void print(int s) 15 { 16 for(RG int i=0;i<lim;++i)printf("%d",(s>>i)&1 ); 17 printf("\n"); 18 } 19 signed main() 20 { 21 scanf("%d%d",&n,&mod); 22 RG int x,y,z,tmp,i,j,k; 23 for(i=2;i<=n;++i) 24 { 25 if(!vis[i])prime[++tot]=i,id[i]=tot; 26 for(j=1;(x=i*prime[j])<=n;++j) 27 {vis[x]=1;if(i%prime[j]==0)break;} 28 } 29 lim=min(8,tot),full=(1<<lim)-1; 30 for(bin[0]=i=1;i<=lim;++i)bin[i]=bin[i-1]<<1; 31 for(i=2;i<=n;++i) 32 { 33 for(x=i,y=0,j=1;j<=lim;++j) 34 if(x%prime[j]==0) 35 { 36 y|=bin[j-1]; 37 while(x%prime[j]==0)x/=prime[j]; 38 } 39 cont[id[x]].push_back(y); 40 } 41 f[0][0]=1; 42 for(x=0,y=cont[0].size();x<y;++x) 43 { 44 memcpy(g[0],f,sizeof(f)); 45 memcpy(g[1],f,sizeof(f)); 46 for(i=full;~i;--i) 47 for(j=full;~j;--j) 48 if((i&j)==0) 49 { 50 if((cont[0][x]&j)==0)g[0][i|cont[0][x]][j]=(g[0][i|cont[0][x]][j]+g[0][i][j])%mod; 51 if((cont[0][x]&i)==0)g[1][i][j|cont[0][x]]=(g[1][i][j|cont[0][x]]+g[1][i][j])%mod; 52 } 53 for(i=full;~i;--i) 54 for(j=full;~j;--j) 55 if((i&j)==0)f[i][j]=(g[0][i][j]+g[1][i][j]-f[i][j])%mod; 56 } 57 for(k=1;k<=tot;++k) 58 { 59 memcpy(g[0],f,sizeof(f)); 60 memcpy(g[1],f,sizeof(f)); 61 for(x=0,y=cont[k].size();x<y;++x) 62 { 63 for(i=full;~i;--i) 64 for(j=full;~j;--j) 65 if((i&j)==0) 66 { 67 if((cont[k][x]&j)==0)g[0][i|cont[k][x]][j]=(g[0][i|cont[k][x]][j]+g[0][i][j])%mod; 68 if((cont[k][x]&i)==0)g[1][i][j|cont[k][x]]=(g[1][i][j|cont[k][x]]+g[1][i][j])%mod; 69 } 70 } 71 for(i=full;~i;--i) 72 for(j=full;~j;--j) 73 if((i&j)==0)f[i][j]=((LL)g[0][i][j]+g[1][i][j]-f[i][j])%mod; 74 } 75 for(i=full;~i;--i) 76 for(j=full;~j;--j) 77 if((i&j)==0)ans=(ans+f[i][j])%mod; 78 printf("%d\n",(ans+mod)%mod); 79 }
刷新指望機率觀的題目
思惟難度不錯,並且以前我是沒見過時望機率用積分的……
雖然出題人說這很套路
1 #include <cstdio> 2 #include <cstring> 3 #define N 35 4 #define mod 998244353 5 #define N2 5000010 6 #define LL long long 7 #define RG register 8 #define MD 2332333 9 struct hash_map 10 { 11 struct node{int state,next,id;}s[N2]; 12 int e,adj[MD]; 13 inline void ins(int state,int id) 14 { 15 RG int pos=state%MD; 16 s[++e].state=state,s[e].next=adj[pos];adj[pos]=e;s[e].id=id; 17 } 18 inline int find(int state) 19 { 20 RG int i,pos=state%MD; 21 for(i=adj[pos];i;i=s[i].next) 22 if(s[i].state==state)return s[i].id; 23 return -1; 24 } 25 }H; 26 struct node 27 { 28 int A[N],n; 29 inline node operator * (const node &a)const 30 { 31 RG int i,j; 32 node c;memset(c.A,0,sizeof(c.A)); 33 c.n=n+a.n; 34 for(i=0;i<=n;++i) 35 for(j=0;j<=a.n;++j) 36 c.A[i+j]=(c.A[i+j]+(LL)A[i]*a.A[j])%mod; 37 return c; 38 } 39 }X[N2]; 40 int tot,T,vis[N],d[N][N],adj[N],C[N][N]; 41 int inv[N],inv2[N],bin[N],cnt1[65546],n,m,A[N],B[N]; 42 inline int count(int s){return cnt1[s&65535]+cnt1[s>>16];} 43 inline int dfs(RG int rt,int G) 44 { 45 RG int ret=bin[rt-1];vis[rt]=T; 46 for(RG int i=1;i<=n;++i) 47 if(vis[i]!=T&&d[rt][i]&&(G&bin[i-1]))ret|=dfs(i,G); 48 return ret; 49 } 50 inline void update(node &a,const node &b,int d) 51 { 52 RG int i,j,nn=b.n+d; 53 memset(A,0,sizeof(A)),memset(B,0,sizeof(B)); 54 for(i=0;i<=d;++i) 55 if(i&1)A[i]=mod-C[d][i];else A[i]=C[d][i]; 56 for(i=0;i<=b.n;++i)for(j=0;j<=d;++j) 57 B[i+j]=(B[i+j]+(LL)b.A[i]*A[j])%mod; 58 for(i=0;i<=nn;++i)a.A[i]=(a.A[i]+B[i])%mod; 59 } 60 inline int getans(RG int G) 61 { 62 int pre=H.find(G); 63 if(pre!=-1)return pre; 64 if(!G) 65 { 66 ++tot,X[tot].n=0,X[tot].A[0]=1;H.ins(G,tot); 67 return tot; 68 } 69 ++T,++tot,H.ins(G,tot); 70 RG int i,u,j,un=0,cid=tot; 71 int block[N]; 72 for(i=1;i<=n;++i) 73 if((G&bin[i-1])&&vis[i]!=T) 74 {u=dfs(i,G);if(u!=G)block[++un]=u;} 75 if(un) 76 { 77 for(X[cid]=X[getans(block[1])],i=2;i<=un;++i) 78 X[cid]=X[cid]*X[getans(block[i])]; 79 return cid; 80 } 81 X[cid].n=count(G); 82 for(i=1;i<=n;++i) 83 if(G&bin[i-1]) 84 update(X[cid],X[getans(G&(~adj[i]))],count(G&adj[i])-1);//考慮每一個點做爲最大值 85 86 for(i=X[cid].n;i;--i)X[cid].A[i]=(LL)inv[i]*X[cid].A[i-1]%mod;//求導 87 RG int sum=0; 88 for(i=X[cid].n;i;--i)sum=(sum+(LL)X[cid].A[i]*inv2[i])%mod;//求導第二部分 89 X[cid].A[0]=(mod+inv2[X[cid].n]-sum)%mod;//考慮最大值<=t/2 90 return cid; 91 } 92 int main() 93 { 94 RG int i,j,a,b; 95 scanf("%d%d",&n,&m); 96 for(bin[0]=i=1;i<=30;++i)bin[i]=bin[i-1]<<1; 97 for(inv[0]=inv[1]=1,i=2;i<=n+5;++i)inv[i]=(LL)(mod-mod/i)*inv[mod%i]%mod; 98 for(inv2[0]=1,i=1;i<=n+5;++i)inv2[i]=(LL)inv2[i-1]*inv[2]%mod; 99 for(i=1;i<=n;++i)adj[i]=bin[i-1]; 100 for(i=1;i<bin[16];++i)cnt1[i]=cnt1[i^(i&-i)]+1; 101 for(i=1;i<=m;++i) 102 scanf("%d%d",&a,&b),d[a][b]=d[b][a]=1,adj[a]|=bin[b-1],adj[b]|=bin[a-1]; 103 for(C[0][0]=1,i=1;i<=n+5;++i) 104 for(C[i][0]=1,j=1;j<=i;++j) 105 C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod; 106 RG int fin=getans(bin[n]-1),ans=0; 107 for(i=0;i<=n;++i) 108 ans=(ans+(LL)inv[n+1]*X[fin].A[i])%mod; 109 for(i=0;i<=n;++i) 110 ans=(ans+(LL)inv[n-i+1]*X[fin].A[i]%mod*(bin[n-i+1]-1))%mod; 111 printf("%d\n",2-ans+mod); 112 }
一開始想了個暴力dp,結果發現本身也漏了很多狀態,也重了很多……
而後就很完蛋啊……發現無法打
最後慫了題解,而後題解和我同樣先考慮一條弧的狀態
可是又不太同樣……他考慮的是「弧兩端由對稱的花分開」,而後枚舉第一個對稱的位置,統計乘積×方案數
我是直接統計一個半圓
而後最後枚舉兩個弧拼起來統計答案
這玩意……堆磚的工做……
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 #include <cstdlib> 5 using namespace std; 6 #define N 50010 7 #define L (1<<19)+10 8 #define mod 998244353 9 #define RG register 10 #define LL long long 11 int n,g[N],f0[N],f1[N],f2[N],pf[N]; 12 inline int quick_mod(int di,int mi) 13 { 14 RG int ret=1; 15 for(;mi;mi>>=1,di=(LL)di*di%mod) 16 if(mi&1)ret=(LL)ret*di%mod; 17 return ret; 18 } 19 int poww[L],rev[L],bin[25],logi[L],inv[L],len=1; 20 inline void dft(int *a,int ra,int opt) 21 { 22 RG int i,j,d=logi[len]-logi[ra],l,tmp,wn,*w,*x,*y; 23 for(i=0;i<ra;++i)if(i<(rev[i]>>d))swap(a[i],a[rev[i]>>d]); 24 for(d=2;d<=ra;d<<=1) 25 for(i=0,l=(d>>1),wn=(opt==1?(len/d):(-len/d));i<ra;i+=d) 26 for(j=0,x=a+i,y=x+l,w=poww+((opt==1)?0:len);j<l;++j,++x,++y,w+=wn) 27 tmp=(LL)(*w)*(*y)%mod,*y=(*x+mod-tmp)%mod,*x=(*x+tmp)%mod; 28 if(opt==-1) 29 for(i=0;i<ra;++i)a[i]=(LL)a[i]*inv[ra]%mod; 30 } 31 int tmp1[L],tmp2[L]; 32 inline int solve1(int l,int r) 33 { 34 if(l==r)return 1; 35 RG int i,mi=l+r>>1,ra=r-l+1,la=1,r1=solve1(l,mi); 36 while(la<(ra+r1))la<<=1; 37 for(i=0;i<r1;++i)tmp1[i]=f0[l+i]; 38 memset(tmp1+r1,0,la-r1<<2);dft(tmp1,la,1); 39 for(i=0;i<ra;++i)tmp2[i]=(LL)g[i]*pf[i]%mod; 40 memset(tmp2+ra,0,la-ra<<2);dft(tmp2,la,1); 41 for(i=0;i<la;++i)tmp2[i]=(LL)tmp2[i]*tmp1[i]%mod; 42 dft(tmp2,la,-1); 43 for(i=mi+1;i<=r;++i)f0[i]=(f0[i]+tmp2[i-l-1])%mod; 44 for(i=0;i<ra;++i)tmp2[i]=(LL)g[i]*pf[i+1]%mod; 45 memset(tmp2+ra,0,la-ra<<2);dft(tmp2,la,1); 46 for(i=0;i<la;++i)tmp1[i]=(LL)tmp2[i]*tmp1[i]%mod; 47 dft(tmp1,la,-1); 48 for(i=mi+1;i<=r;++i)f1[i]=(f1[i]+tmp1[i-1-l])%mod; 49 for(i=0;i<r1;++i)tmp1[i]=f1[l+i]; 50 memset(tmp1+r1,0,la-r1<<2);dft(tmp1,la,1); 51 for(i=0;i<la;++i)tmp2[i]=(LL)tmp2[i]*tmp1[i]%mod; 52 dft(tmp2,la,-1); 53 for(i=mi+1;i<=r;++i)if(i-l-3>=0)f0[i]=(f0[i]+tmp2[i-l-3])%mod; 54 for(i=mi+1;i<=r;++i)f2[i]=(f2[i]+tmp2[i-1-l])%mod; 55 for(i=0;i<ra;++i)tmp2[i]=(LL)g[i]*pf[i+2]%mod; 56 memset(tmp2+ra,0,la-ra<<2);dft(tmp2,la,1); 57 for(i=0;i<la;++i)tmp1[i]=(LL)tmp2[i]*tmp1[i]%mod; 58 dft(tmp1,la,-1); 59 for(i=mi+1;i<=r;++i)if(i-l-3>=0)f1[i]=(f1[i]+tmp1[i-l-3])%mod; 60 for(i=0;i<r1;++i)tmp1[i]=f2[l+i]; 61 memset(tmp1+r1,0,la-r1<<2);dft(tmp1,la,1); 62 for(i=0;i<la;++i)tmp2[i]=(LL)tmp2[i]*tmp1[i]%mod; 63 dft(tmp2,la,-1); 64 for(i=mi+1;i<=r;++i)if(i-l-3>=0)f2[i]=(f2[i]+tmp2[i-l-3])%mod; 65 solve1(mi+1,r); 66 return ra; 67 } 68 signed main() 69 { 70 RG int i; 71 scanf("%d",&n);while(len<(n<<2))len<<=1; 72 for(bin[0]=i=1;i<=20;++i)bin[i]=bin[i-1]<<1; 73 for(i=0;i<=18;++i)logi[bin[i]]=i; 74 for(inv[2]=((mod+1)>>1),i=2;i<=18;++i)inv[bin[i]]=(LL)inv[bin[i-1]]*inv[2]%mod; 75 for(i=0;i<len;++i) 76 if(i&1)rev[i]=(rev[i>>1]>>1)|(len>>1); 77 else rev[i]=rev[i>>1]>>1; 78 poww[0]=poww[len]=1,poww[1]=quick_mod(3,(mod-1)/len); 79 for(i=2;i<len;++i)poww[i]=(LL)poww[i-1]*poww[1]%mod; 80 for(i=1;i<=n+2;++i)pf[i]=(LL)i*i%mod; 81 g[0]=1,g[1]=0,g[2]=1,g[3]=0; 82 for(i=4;i<=n;i+=2)g[i]=(g[i-2]+g[i-4])%mod; 83 f0[0]=0,f1[0]=1,f2[0]=4; 84 for(i=1;i<=n;++i) 85 {f0[i]=(LL)g[i]*pf[i]%mod;f1[i]=(LL)g[i]*pf[i+1]%mod;f2[i]=(LL)g[i]*pf[i+2]%mod;} 86 solve1(0,n); 87 RG int ans=(LL)(g[n-1]+g[n-3])*pf[n-1]%mod*n%mod; 88 for(i=3;i<n;++i)ans=(ans+(LL)g[i-2]*pf[i-2]%mod*f0[n-i]%mod*(i-1))%mod; 89 for(i=3;i<n;++i)ans=(ans+(LL)2*g[i-3]*pf[i-2]%mod*f1[n-i-1]%mod*(i-1))%mod; 90 for(i=4;i<=n-2;++i)ans=(ans+(LL)g[i-4]*pf[i-2]%mod*f2[n-i-2]%mod*(i-1))%mod; 91 printf("%d\n",ans); 92 }
2017清華集訓的題目
從dalao們的博客來看,是最水的一道題?
那仍是我太菜了233333……懟了好幾天最後仍是慫了題解
我一開始想的是分權值討論,而後從大到小搞
而後一開始覺得相同區間的能夠合併,而後就是一個$v^{len}-(v-1)^{len}$
結果發現不太對……若是區間有重複的話就會完蛋
到最後這個貢獻我也不會dp
發現咱們能夠離散以後分段考慮,一段以內的貢獻是同樣的
而後裸轉移是$O(n)$的,能夠經過前綴和而後搞個倍數乘除一下優化成$O(1)$
太神了……
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 #define RG register 6 #define LL long long 7 #define mod 998244353 8 #define N 2010 9 inline int quick_mod(int di,int mi) 10 { 11 RG int ret=1; 12 for(;mi;mi>>=1,di=(LL)di*di%mod) 13 if(mi&1)ret=(LL)ret*di%mod; 14 return ret; 15 } 16 struct node 17 { 18 int l,r,val; 19 node(int a=0,int b=0,int c=0){l=a,r=b,val=c;} 20 }q[N],s[N]; 21 int tmp[N],keypt[N],len[N],pt[N]; 22 int lcnt,cnt,scnt,A; 23 int f[2][N],g[2][N],lim[N]; 24 int l[N],sl[N]; 25 int pre1[N],pre2[N],pre3[N],inv2[N],inv3[N]; 26 inline int dp(int val) 27 { 28 if(val==1)return 1; 29 RG int i,j,cur=0; 30 f[0][0]=g[0][0]=1;s[0].r=0; 31 pre1[0]=pre2[0]=pre3[0]=inv2[0]=inv3[0]=1; 32 for(i=1;i<=lcnt;++i) 33 { 34 g[0][i]=1,f[0][i]=0,sl[i]=sl[i-1]+l[i]; 35 pre1[i]=(quick_mod(val,l[i])-quick_mod(val-1,l[i])+mod)%mod; 36 pre2[i]=quick_mod(val-1,sl[i]);inv2[i]=quick_mod(pre2[i],mod-2); 37 pre3[i]=quick_mod(val,sl[i]);inv3[i]=quick_mod(pre3[i],mod-2); 38 } 39 for(i=1;i<=scnt;++i) 40 { 41 cur^=1; 42 for(j=0;j<s[i].l;++j)f[cur][j]=g[cur][j]=0; 43 for(j=s[i].l;j<=s[i].r;++j) 44 { 45 f[cur][j]=f[cur^1][j]; 46 if(j>s[i-1].r) 47 f[cur][j]=(f[cur][j]+ (LL)g[cur^1][s[i-1].r] * pre2[s[i-1].r] %mod * pre3[j-1] %mod * inv3[s[i-1].r] %mod * pre1[j] )%mod; 48 g[cur][j]=(g[cur][j-1]+(LL)f[cur][j]*inv2[j])%mod; 49 } 50 for(j=s[i].r+1;j<=lcnt;++j)g[cur][j]=g[cur][j-1],f[cur][j]=0; 51 } 52 return (LL)g[cur][lcnt]*pre2[lcnt]%mod; 53 } 54 inline bool mt(const node &a,const node &b) 55 {return a.val==b.val?a.r<b.r:a.val<b.val;} 56 inline void work() 57 { 58 RG int i,j,n,m,a,b,val,ge=0,ans; 59 scanf("%d%d%d",&n,&m,&A); 60 for(i=1;i<=m;++i) 61 { 62 scanf("%d%d%d",&a,&b,&val); 63 q[i]=node(a,b,val); 64 tmp[++ge]=a,tmp[++ge]=b; 65 } 66 tmp[++ge]=1,tmp[++ge]=n; 67 sort(tmp+1,tmp+ge+1),ge=unique(tmp+1,tmp+ge+1)-tmp-1; 68 for(cnt=0,i=1;i<=ge;++i) 69 { 70 keypt[++cnt]=tmp[i],len[cnt]=1; 71 if(i<ge&&tmp[i]+1<tmp[i+1]) 72 keypt[++cnt]=tmp[i]+1,len[cnt]=tmp[i+1]-tmp[i]-1; 73 } 74 sort(q+1,q+m+1,mt); 75 bool can;RG int v,maxn; 76 memset(lim,0,sizeof(lim)); 77 for(i=1;i<=m;++i) 78 { 79 q[i].l=lower_bound(keypt+1,keypt+cnt+1,q[i].l)-keypt; 80 q[i].r=lower_bound(keypt+1,keypt+cnt+1,q[i].r)-keypt; 81 for(can=0,maxn=0,j=q[i].l;j<=q[i].r;++j) 82 if(!lim[j])lim[j]=q[i].val,can=1; 83 else maxn=max(maxn,lim[j]); 84 if(!can&&maxn<q[i].val){puts("0");return;} 85 } 86 ans=1; 87 for(i=1;i<=m;) 88 { 89 for(scnt=0,v=q[i].val,j=i;v==q[j].val&&j<=m;++j)s[++scnt]=q[j]; 90 for(i=j,lcnt=0,j=1;j<=cnt;++j) 91 if(lim[j]==v)l[pt[j]=++lcnt]=len[j]; 92 for(j=1;j<=scnt;++j) 93 { 94 while(lim[s[j].l]!=v)++s[j].l; 95 while(lim[s[j].r]!=v)--s[j].r; 96 s[j].l=pt[s[j].l],s[j].r=pt[s[j].r]; 97 } 98 ans=(LL)ans*dp(v)%mod; 99 } 100 for(i=1;i<=cnt;++i) 101 if(!lim[i])ans=(LL)ans*quick_mod(A,len[i])%mod; 102 printf("%d\n",ans); 103 } 104 int main() 105 { 106 RG int i,j,t; 107 scanf("%d",&t); 108 while(t--)work(); 109 }
依然是清華集訓的題目
依舊沒作出來
依舊慫了題解
咱們從第三個部分分開始考慮,只輸出根的答案
什麼樣的狀況下能有答案呢?
至關於咱們要把兒子們分紅兩半,讓他們數量同樣
而且可以互相抵消,最後讓心停在根節點
那麼……確定是最大的那個兒子最難被抵消
咱們考慮最大的那個兒子有多大
而後看其餘兒子能不能把它抵消掉
咱們定義$f(i)$爲以$i$爲根的子樹最多能抵消多少對生長的貢獻
若是一個點子樹大小是偶數,
那麼咱們考慮最大的那個子樹,咱們確定優先把它消掉
若是它能被消掉,咱們其餘子樹裏的就能夠對着叉,怎麼叉均可以叉掉
那麼咱們考慮,優先讓這個最大的子樹$s1$自相殘殺,再用其餘兒子來幫他
最多能消掉最大子樹的節點數就是$size[rt]-1-size[s1]+f[s1]*2$
那麼若是這個數大於等於$size[s1]$,這個樹就能夠所有消掉,$f[rt]=(size[rt]-1)/2$
而對於處理不是根的點,咱們把根到它縮成一條路徑,這樣和剛纔計算根是同樣的
而後就能夠打了,複雜度是$O(Tn)$的
1 #include <cstdio> 2 #include <cstring> 3 using namespace std; 4 #define RG register 5 #define N 100010 6 char B[1<<15],*S=B,*T=B; 7 #define getc (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:*S++) 8 inline int read() 9 { 10 RG int x=0;RG char c=getc; 11 while(c<'0'|c>'9')c=getc; 12 while(c>='0'&c<='9')x=10*x+(c^48),c=getc; 13 return x; 14 } 15 int W,n,e,adj[N]; 16 struct edge{int zhong,next;}s[N<<1]; 17 inline void add(int qi,int zhong) 18 {s[++e].zhong=zhong;s[e].next=adj[qi];adj[qi]=e;} 19 int ans[N],size[N],maxn[N],son1[N],son2[N]; 20 inline void dfs1(int rt,int fa) 21 { 22 size[rt]=1;son1[rt]=son2[rt]=0; 23 for(RG int u,i=adj[rt];i;i=s[i].next) 24 if((u=s[i].zhong)!=fa) 25 { 26 dfs1(u,rt);size[rt]+=size[u]; 27 if(size[u]>size[son1[rt]])son2[rt]=son1[rt],son1[rt]=u; 28 else if(size[u]>size[son2[rt]])son2[rt]=u; 29 } 30 if(son1[rt]) 31 { 32 RG int left=size[rt]-1-size[son1[rt]]; 33 if(size[son1[rt]]<=left+(maxn[son1[rt]]<<1))maxn[rt]=size[rt]-1>>1; 34 else maxn[rt]=maxn[son1[rt]]+left; 35 } 36 } 37 inline void dfs2(int rt,int fa,int dep,int maxson) 38 { 39 if((n-dep)&1)ans[rt]=0; 40 else 41 { 42 RG int bs=((size[maxson]>size[son1[rt]])?maxson:son1[rt]); 43 ans[rt]=(size[bs]<=n-dep-size[bs]+(maxn[bs]<<1)); 44 } 45 for(RG int u,i=adj[rt];i;i=s[i].next) 46 if((u=s[i].zhong)!=fa) 47 if(u==son1[rt])dfs2(u,rt,dep+1, (size[maxson]>size[son2[rt]])?maxson:son2[rt]); 48 else dfs2(u,rt,dep+1, (size[maxson]>size[son1[rt]])?maxson:son1[rt]); 49 } 50 inline void work() 51 { 52 RG int i,a,b; 53 n=read(); 54 e=0;memset(adj,0,n+1<<2); 55 memset(son1,0,n+1<<2); 56 memset(son2,0,n+1<<2); 57 for(i=1;i<n;++i) 58 a=read(),b=read(),add(a,b),add(b,a); 59 dfs1(1,0);dfs2(1,0,1,0); 60 if(W==3)printf("%d",ans[1]); 61 else for(i=1;i<=n;++i)printf("%d",ans[i]); 62 printf("\n"); 63 } 64 int main() 65 { 66 W=read();int t=read(); 67 while(t--)work(); 68 }
這題的動歸定義其實沒那麼難
可是……這是個仙人掌啊……仙人掌啊……
因此代碼實現就特別完蛋了
我想的是把每一個環拆成鏈,維護鏈頂鏈底的狀態
打了180行仍是錯的,細節還賊多
這就比較坑了……因而我找了個標程,發現人家和個人定義同樣
可是隻有我傻傻的手動分類討論,人家用for循環枚舉加幾個簡單的ifelse判斷就好了
因而深入的研究了一下std……仙人掌的題目我還的確沒作過,這題是不錯的入門(?)題
code:
1 #include <cstdio> 2 #include <cstring> 3 using namespace std; 4 #define RG register 5 #define inf 0x3f3f3f3f 6 #define N 2510 7 inline int max(int a,int b){return a>b?a:b;} 8 int n,m,e,adj[N]; 9 struct edge{int zhong,next;}s[N<<2]; 10 inline void add(int qi,int zhong) 11 {s[++e].zhong=zhong;s[e].next=adj[qi];adj[qi]=e;} 12 int size[N],f[N][N][3][3],vis[N],top[N]; 13 int g[N][3][3]; 14 inline void dfs(int rt,int fa) 15 { 16 size[rt]=1; 17 ++vis[rt]; 18 f[rt][0][0][0]=f[rt][1][1][0]=0,f[rt][0][2][0]=1; 19 bool isend=0; 20 for(RG int u,i=adj[rt];i;i=s[i].next) 21 { 22 if(vis[u=s[i].zhong]<2&&u!=fa) 23 if(!vis[u]) 24 { 25 dfs(u,rt); 26 memset(g,-0x3f,sizeof(g)); 27 if(top[u]) 28 { 29 if(top[u]==rt) 30 { 31 for(RG int j=size[rt];~j;--j) 32 for(RG int k=size[u];~k;--k) 33 for(RG int a=0;a<3;++a) 34 for(RG int b=0;b<3;++b)if(a^b^3) 35 for(RG int c=0;c<3;++c) 36 g[j+k][a][c]=max(g[j+k][a][c],f[rt][j][a][c]+f[u][k][b][a]); 37 } 38 else 39 { 40 top[rt]=top[u]; 41 for(RG int j=size[rt];~j;--j) 42 for(RG int k=size[u];~k;--k) 43 for(RG int a=0;a<3;++a) 44 for(RG int b=0;b<3;++b)if(a^b^3) 45 for(RG int c=0;c<3;++c) 46 g[j+k][a][c]=max(g[j+k][a][c],f[rt][j][a][0]+f[u][k][b][c]); 47 } 48 } 49 else 50 { 51 for(RG int j=size[rt];~j;--j) 52 for(RG int k=size[u];~k;--k) 53 for(RG int a=0;a<3;++a) 54 for(RG int b=0;b<3;++b)if(a^b^3) 55 for(RG int c=0;c<3;++c) 56 g[j+k][a][c]=max(g[j+k][a][c],f[rt][j][a][c]+f[u][k][b][0]); 57 } 58 size[rt]+=size[u]; 59 for(RG int j=size[rt];~j;--j) 60 for(RG int a=0;a<3;++a) 61 for(RG int b=0;b<3;++b) 62 f[rt][j][a][b]=g[j][a][b]; 63 } 64 else isend=1,top[rt]=u; 65 } 66 if(isend) 67 for(RG int i=0;i<=size[rt];++i) 68 { 69 for(RG int a=0;a<3;++a)f[rt][i][a][2]=f[rt][i][a][1]=f[rt][i][a][0]; 70 f[rt][i][1][2]=f[rt][i][2][1]=-inf; 71 } 72 ++vis[rt]; 73 } 74 int main() 75 { 76 // freopen("Ark.in","r",stdin); 77 RG int i,a,b; 78 scanf("%d%d",&n,&m); 79 for(i=1;i<=m;++i) 80 scanf("%d%d",&a,&b),add(a,b),add(b,a); 81 memset(f,-0x3f,sizeof(f)); 82 dfs(1,0); 83 for(i=0;i<=n;++i) 84 printf("%d ",max(max(f[1][i][0][0],f[1][i][1][0]),f[1][i][2][0])); 85 }
這題太神了!!!!!
膜拜出題的伊朗神犇
題目顯然能夠分紅兩問,第一部分統計最後都有誰能拿到假金塊
第二部分用第一部分的結果統計答案
但是我兩個部分都不會啊
這可怎麼辦呢
而後在思考半個多小時無果的狀況下我打開了題解
看了快倆小時纔看懂那是什麼……
大概的推導過程是這樣的:
首先咱們考慮兩個被有向邊連接的點$u$和$v$,設其大小爲$s_{u}$和$s_{v}$,定義$g=gcd(s_{u},s_{v})$
那麼若是$i \% g == j \% g$,那麼i和j之間應該有邊,即「若是i有金塊,那麼i會給j一個假金塊」
這樣的貢獻是很顯然的。那麼咱們來考慮複雜一點的狀況:若是有鏈$u->v->w$,如今咱們考慮$u$對$w$的貢獻。
圖中定義$g=gcd(s_{u},s_{v})$:
我先解釋一下新出現的f是啥:
咱們定義$f(v,g)$爲一個假想幫派,其大小爲$g$,而且知足若是幫派$v$中第$i$我的有金塊,那麼$f(v,g)$中第$i\%g$我的就有金塊
這樣的話,因爲u對w的貢獻應該體如今「u使v出現了v本身沒有的金塊,而後v又把這個金塊給了w」
那麼咱們$u$對$w$的貢獻就能夠體現爲一個$f(u,gcd(s_{u},s_{v}))$對$w$的貢獻
這樣也不難理解……由於若是v用從u那裏拿到的金塊去貢獻w,那貢獻的單元必定是$f(u,gcd(s_{u},s_{v}))$
那麼咱們推廣一下:假若有一條鏈$w_{1}->w_{2}->......->w_{n}$,
那麼$w_{1}$對$w_{n}$的貢獻應該能夠體現爲$f(w_{1},gcd(s_{w_{1}},s_{w_{2}},.....s_{w_{n-1}}))$
可是原圖並不保證是個$DAG$,因此咱們考慮,若是有一個閉合迴路$v->w_{1}->w_{2}->......->w_{n}->v$,
那麼$v$對本身的貢獻是什麼呢?
類比上面的作法,應該能夠體現爲$f(v,gcd(s_{v},s_{w_{1}},s_{w_{2}},.....s_{w_{n}}))$
那麼咱們如今能夠處理環內部的貢獻了,那麼咱們考慮把環縮成點,考慮總體對其餘聯通塊的貢獻
結合剛纔咱們的定義,如今咱們應該有一個對於某個環定義的$h(S,g)$,
依然是大小爲g的幫派,可是如今只要強連通份量S中有某一個元素$w_{i}$中存在i有金塊,那麼h中$i\%g$就有一個金塊
在定義了h以後,咱們計算環中的答案也容易了:對於$S$中幫派v,若是$h(S,g)$中第i個有金塊,那麼全部$v_{j}(j\%g==i)$都會有金塊,
由於環上每一個點也會被這個環全部點進行貢獻
再考慮$SCC$之間的貢獻:咱們縮完點以後,原圖變成了$DAG$徹底圖
那麼這樣的圖裏面一個存在一條哈密頓路徑,即通過全部點的路徑。這是很顯然的。
那麼咱們按拓撲序遍歷這個路徑,每次用前面一個$SCC$的$h$去貢獻後面一個$SCC$的$h$
這樣咱們就獲得了最後的每一個$h$,再按剛纔說的方法統計就能夠求出每一個幫派最多有多少金塊了
那麼對於每一個幫派,咱們如今獲得了它最少&最多能夠賣出多少金塊,分別定義爲$mn$和$mx$
而後考慮統計答案,咱們枚舉每一個幫派$v$做爲被逮捕的$b$個幫派中實力最小的進行統計,
首先值得注意的是,咱們這時候固定每一個幫派的實力就是它能賣出的最多的金塊
由於若是對於某種前a個的方案中,有的幫派沒有賣出$mx$個,那麼咱們能夠把他們換成賣出$mx$個,這樣a集合是不變的。
因此咱們就欽定幫派們都賣出了mx個
那麼咱們考慮另一個幫派$u$,若是……
$mn_{u}>mx_{v}$,那麼u必定實力比v大,設這樣的一共有c1個幫派
$mx_{u}>mx_{v}$,那麼u可能實力比v大,設這樣的一共有c2個幫派(這裏,當$mx$相等的時候,咱們按照編號大小比較,這樣就不重不漏了)
因此咱們枚舉有幾個可能比v實力大的幫派最後被選中,設個數爲$j$
那麼對答案的貢獻就是$C(c2,j)*C(c1,b-1-j)$
咱們只要限制好枚舉邊界($j<b,j<=c2,j+c1<a$)就能夠統計答案了
這樣咱們就解決了這道神題!
這題給個人觸動是不小的……
第一部分推導的思路很是清晰,從簡單狀況發現規律,輔助變量$f$和$h$的引入,一條邊到鏈到環的推導……這樣的推導真的讓人感受很是暢快。
還有第二部分的統計答案,巧妙的枚舉了實力最小的幫派,使得統計變得輕鬆。
這題目真的是666啊……
最後附上code:
1 #include <cstdio> 2 #include <cstring> 3 #include <vector> 4 using namespace std; 5 #define RG register 6 #define N 5010 7 #define mod 1000000007 8 #define LL long long 9 #define min(x,y) ((x)<(y)?(x):(y)) 10 char B[1<<15],*S=B,*T=B; 11 #define getc (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:*S++) 12 inline int read() 13 { 14 RG int x=0;RG char c=getc; 15 while(c<'0'|c>'9')c=getc; 16 while(c>='0'&c<='9')x=10*x+(c^48),c=getc; 17 return x; 18 } 19 inline int get01() 20 { 21 RG char c=getc; 22 while(c<'0'|c>'1')c=getc; 23 return c^48; 24 } 25 int n,a,b,ge[N],e,adj[N]; 26 vector<int>mem[N],con[N],can[N]; 27 struct edge{int zhong,next;}s[N*N>>1]; 28 inline void add(int qi,int zhong) 29 {s[++e].zhong=zhong;s[e].next=adj[qi];adj[qi]=e;} 30 int dfn[N],low[N],num,sta[N],top; 31 int sz[N],belong[N],tot,mn[N],mx[N],C[N][N]; 32 inline int gcd(int a,int b){return b==0?a:gcd(b,a%b);} 33 #define vi vector<int>::iterator 34 inline void tarjan(int rt) 35 { 36 RG int i,u; 37 dfn[rt]=low[rt]=++num;sta[++top]=rt; 38 for(i=adj[rt];i;i=s[i].next) 39 if(!dfn[u=s[i].zhong])tarjan(u),low[rt]=min(low[rt],low[u]); 40 else if(!belong[u])low[rt]=min(low[rt],dfn[u]); 41 if(dfn[rt]==low[rt]) 42 { 43 ++tot; 44 do belong[u=sta[top--]]=tot,sz[tot]=gcd(sz[tot],ge[u]),con[tot].push_back(u);while(u!=rt); 45 can[tot].resize(sz[tot]); 46 for(vi it=con[tot].begin();it!=con[tot].end();++it) 47 for(vi j=mem[*it].begin();j!=mem[*it].end();++j) 48 can[tot][*j % sz[tot]]=1; 49 } 50 } 51 int main() 52 { 53 // freopen("Ark.in","r",stdin); 54 RG int i,j,x,ans=0,cnt1,cnt2; 55 n=read();a=read();b=read(); 56 for(i=1;i<=n;++i)for(j=1;j<=n;++j)if(get01())add(i,j); 57 for(i=1;i<=n;++i) 58 for(ge[i]=read(),j=0;j<ge[i];++j) 59 if(get01())++mn[i],mem[i].push_back(j); 60 for(i=1;i<=n;++i)if(!dfn[i])tarjan(i); 61 for(i=tot;i>1;--i) 62 for(sz[i-1]=gcd(sz[i],sz[i-1]),j=0;j<sz[i];++j) 63 if(can[i][j])can[i-1][ j % sz[i-1] ]=1; 64 for(i=1;i<=n;++i) 65 for(x=belong[i],j=0;j<ge[i];++j) 66 mx[i]+=can[x][j%sz[x]]; 67 for(C[0][0]=i=1;i<=n;++i) 68 for(C[i][0]=j=1;j<=i;++j) 69 C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod; 70 for(i=1;i<=n;++i) 71 { 72 for(cnt1=0,j=1;j<=n;++j)if(mn[j]>mx[i])++cnt1; 73 if(cnt1>=a)continue; 74 for(cnt2=0,j=1;j<=n;++j) 75 if(mn[j]<=mx[i]&&(mx[j]>mx[i]||(mx[j]==mx[i]&&j>i)))++cnt2; 76 for(j=min(b-1,min(cnt2,a-cnt1-1));j>=0;--j) 77 ans=(ans+(LL)C[cnt2][j]*C[cnt1][b-1-j])%mod; 78 } 79 printf("%d\n",ans); 80 }
其實還有不少很棒的題目沒有來得及寫詳細的題解
可是……接下來還要去鹽焗其餘知識,沒有時間了,就涼涼了……
在這裏作個小總結……
dp問題大概分爲兩類,計數和最優化,沒了:)
寫一些零散的思路吧
dp咱們首先要有個狀態
怎麼設計狀態是困擾廣大OIER的千古難題
因此我也不會23333
不過通常來講,咱們dp要找一些關鍵的量進行限制
通常限制的這些量會對結果產生影響……之類的
好比不少序列上的問題定義「最後一個在i」等等
我記得以前看見過一句很好的話:
「對於不少構造題和dp題,不妨先想一想最優解是什麼樣子的,看看它有什麼特徵,再考慮如何設計狀態&轉移」
計數題的最大套路就是容斥了……
感受容斥原理的應用能夠「以時間換思考難度」,
使得原本根本無法處理的限制變得可作
同時,容斥原理在不少題目中仍是正確性的保證
好比:
uoj193的環套樹計數咱們要枚舉葉子,
可是可能剩下的點在有的聯邊狀況下也變成了葉子,因而要容斥
uoj37的DAG計數同理,
枚舉入度爲0的點以後咱們也不知道剩下的點裏面有沒有入度爲0的點,因而要容斥
與容斥相對的處理方法就是考慮「計數如何不重不漏」了
這個就要用心考察每個變量,看看枚舉什麼能夠區分每一種狀態了……
很難說這個東西具體怎麼用,可是我感受在作了上面兩個uoj以後有一點點容斥的手感了
對於全部題目均可能適用的方法:正難則反(或者叫補集轉換?)
第一次具體意識到「正難則反」是看到clj老師的計數pdf的時候
那些題目真的把我碾爆了QAQ
有的時候,若是咱們正着處理合法(不合法)很差處理,而總狀態很好計算,
咱們就能夠思考反過來處理不合法(合法)狀態能不能行,而後就沒準能作了
比較經典的是uoj37,這個題從正着dp「對於某個點集,其誘導子圖的SCC數量」轉換成了反着dp縮點以後DAG的方案數
還有昨天看的wc2007石頭剪刀布,不直接統計三元環而是反過來統計$C(n,3)-cnt$(不是三元環)
對於全部題目均可能適用的方法:模型轉換
這個就更加玄乎了
通常是從本來的問題,尋找其等價問題進行處理,從而簡化問題
大多數時候,咱們根本不知道如何模型轉換
可是就算是鹹魚咱們也要賭一賭國運
聯想一下題目中的某些條件能不能換成別的等價的問題而後處理
我印象中最典型的就是權值轉計數類問題了
咱們給予題設權值一個實際意義,而後就把難處理的權值轉成了計數問題
好比有一次模擬賽,權值是「len×cnt1」,咱們就計數「任選一個染綠,再任選一個1染紅(可重複染色),那麼方案數就是len×cnt1
還有一次是權值是數量的平方,因而咱們轉換成二元組計數,維護兩個獨立的狀態,
他們能夠分別轉移,最後答案就是平方後的結果
還有uoj193,這個權值定義是「環套樹非葉子節點個數」,
咱們就計數「每一個點能夠染黑白兩種顏色,葉子只能染白」的數量,
而後正難則反枚舉黑葉子集合容斥
這種轉換彷佛很常見的……可是該想不出來還想不出來
網格圖利器:插頭dp(輪廓線)
不少時候一個輪廓線幹上去咱們就會發現轉移特別舒服
我的感受經常使用於「須要決策每一個格子放什麼」的問題
不必定只用於傳統的逐格轉移
咱們能夠維護已選集合的輪廓(HEOI2018D1T1),也能夠按行轉移
總之網格圖上輪廓線仍是有前途的233
指望機率怎麼作?
首先一個抓手就是指望的線性性
好比,不少題目都定義「在第i輪尚未結束的機率」,
而後利用指望的線性性加起來這就是指望輪數
還有一個就是積分的應用
以及先後綴不必定用於優化dp,狀態定義裏使用先後綴也許能夠寫出方便轉移的式子
此次沒作到指望機率的題目,還須要增強啊……
有一個很清真的想法就是「對於任何東西均可以定義函數」
不必定dp只能對下標定義啊……
好比我能夠對於一張圖定義一個函數,而後這個函數可以轉移之類的
也就是說,我把全部東西都當作元素,這樣也許能找到元素之間的關係來轉移
數據範圍極小怎麼辦?
咱們仍是別想爆搜了233333
那麼咱們通常考慮狀壓
狀壓的好幫手就是容斥(子集反演)啦
最近發現了狀壓的新套路
好比,當狀態集合有2個,可是咱們能發現包含關係的時候,能夠壓成3進制來作/直接哈希表硬上
以及一些小科技:fwt;枚舉子集;用lowbit枚舉全部的1而不是直接枚舉判斷是否是1
差分的應用也是特別6的
從原來的序列轉換成維護差分數組,可能大大減小可能狀態/維護難度
好比說單調的先後綴最值,以及一些單調不降的數組,咱們能夠用其差分值表明原序列
很大的可能就是變成了01而後能夠狀壓了
權值範圍很小,還和二進制有關?按位考慮也許是個好方法
數學(gcd,根號相關)dp題?想跟根號有關的性質,找到突破口
若是題設目標是對環上東西的一個DP,有一種可能的狀態定義是
「對於一段圓弧(把環拆成鏈),其兩端的狀態分別是xx和yy的計數/最優解」
第一次見到這種想法是在cf848e上
今天聽wq講了bzoj3746加深了一點點印象
可是感受若是遇到題仍是可能想不起來啊……
這其實就把原來環上的問題簡化爲序列了,就能夠用類區間dp的方法dp了
優化方法:改變數組定義
改變數組定義……看命了,能不能想出來的確是智商的問題
優化方法:輔助數組/預處理
有的時候咱們會以爲dp細節不少,手動討論很煩?
曾經我見過的一個不錯的方法就是寫4個calc函數而後來回來去的調用
這樣的話代碼很是簡潔,而且思路也很清晰
我以爲我的的一大缺點就是太喜歡手動討論了
這樣的話代碼會很長,細節也更多更不容易調試
因此……要多試着改變這一點
優化方法;決策單調性(單調隊列/棧,四邊形不等式)
這個方法彷佛我分配的科技點比較少23333
太玄了,也說很差什麼就必定能用,什麼就必定不能用
須要咱們觀察轉移方程,有單調性就能用唄……
優化方法:log((cdq)分治,(wqs)二分/三分)
其實二分三分算是決策單調性?
這樣通常能夠把本來的$n^{2}$變成$nlogn$,很是優秀
upd:2018-4-17
今天考試的T1(loj6032)很是不錯
提示咱們還有一種可行的模型轉化方法是創建分治結構/重構樹
我能想到的應用是克魯斯卡爾重構樹,最小割樹,以及多點最短路
好比這個dp,就是把原來的序列建成重構樹,
利用「建出來的區間兩側隔板高度都比它高,不用考慮邊界」這一優秀性質來dp
同時wq想到的處理方法也是很是不錯的:把條件和隔板一塊兒排序,
直接找到每一個條件對應在哪一個節點被利用
優化方法;矩陣乘法(自動機)/倍增/特徵多項式
若是咱們發現dp過程和當前位置i沒有什麼關係,即每一步的轉移都是相似的
那麼咱們能夠考慮用上面的方式來加速轉移
優化方法;先後綴和,卷積
這個有時候能夠經過化簡/變形dp轉移方程發現特定形式,從而發現優化方法
優化方法:數據結構
打着打着就發現從dp題變成了數據結構題23333
不過有的時候用數據結構的確能夠從枚舉轉移變成直接查詢轉移,優化複雜度
大概用於「用來轉移的狀態處於一個連續區間」的時候
按推薦等級排序~(level大的更加666)
$Level 6$ 共11題 強力推薦
uoj37 uoj193 uoj316 uoj372
bzoj3746 codechef_SEPT11_CNTHEX
cf804F cf855G cf923G cf865E
TopCoder_SRM_641_Hard(有興趣能夠考慮一下增強版問題:n<=1e5)
$Level 5$ 共11題
cf848e cf814E cf938F bzoj3326
uoj141 uoj348 loj553 bzoj1435
Topcoder_SRM_639_Div1_Level2
Topcoder_SRM_547_Div1_Level3
bzoj4871
$Level 4$ 共13題
loj2330 loj2331 bzoj4712 bzoj4513
cf903F cf814D cf755G cf379G uoj370
cf917C cf917D cf722E cf590D
$Level 3$ 共5題
loj2325 uoj129 uoj110 uoj300
uoj139 hihoCoder_challenge_19A/1279
$Level 2$ 共1題
loj6301
$Level 1$ 共1題
cf814C
$Unrated$
(我沒有作也沒有想因此沒辦法評級咯……)
hdu4809 51nod1684 loj2450 bzoj3830
bzoj3711 bzoj3717 bzoj2216 bzoj2091
bzoj3831 hdu5513 loj6274 uoj140
cf773f uoj11 cf468e bzoj4036
bzoj4557 bzoj4859 bzoj4818 loj520
loj2321 loj2324 loj2180 loj2255
loj530 loj515
final update:2018-4-17 16:08:31