思惟題練習專場-DP篇(附題表)

轉載請註明原文地址http://www.cnblogs.com/LadyLex/p/8536399.html html

據說今年省選很可怕?刷題刷題刷題node

省選已經結束了可是咱們要繼續刷題刷題刷題數組

目標是「有思惟難度的DP題」!數據結構

一,uoj316

  這個不用多說……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 }
uoj316

二,bzoj3326

  題目模型不算特別新的數位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 }
bzoj3326

三,bzoj4513

  二進制的數位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 }
bzoj4513

四,uoj141

  很棒的插頭(輪廓線)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 }
uoj141

五,uoj129

  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 }
uoj129

六,uoj372

  刷新指望機率觀的題目

  思惟難度不錯,並且以前我是沒見過時望機率用積分的……

  雖然出題人說這很套路

  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 }
uoj372

七,codeforces848E

  一開始想了個暴力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 }
cf848E

八,LOJ2331

  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 }
LOJ2331

九,LOJ2330

  依然是清華集訓的題目

  依舊沒作出來

  依舊慫了題解

  咱們從第三個部分分開始考慮,只輸出根的答案

  什麼樣的狀況下能有答案呢?

  至關於咱們要把兒子們分紅兩半,讓他們數量同樣

  而且可以互相抵消,最後讓心停在根節點

  那麼……確定是最大的那個兒子最難被抵消

  咱們考慮最大的那個兒子有多大

  而後看其餘兒子能不能把它抵消掉

  咱們定義$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 }
LOJ2330

十,codeforces379E

  這題的動歸定義其實沒那麼難

  可是……這是個仙人掌啊……仙人掌啊……

  因此代碼實現就特別完蛋了

  我想的是把每一個環拆成鏈,維護鏈頂鏈底的狀態

  打了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 }
codeforces379G

十一,codeforces804F

  這題太神了!!!!!

  膜拜出題的伊朗神犇

  題目顯然能夠分紅兩問,第一部分統計最後都有誰能拿到假金塊

  第二部分用第一部分的結果統計答案

  但是我兩個部分都不會啊

  這可怎麼辦呢

  而後在思考半個多小時無果的狀況下我打開了題解

  看了快倆小時纔看懂那是什麼……

  大概的推導過程是這樣的:

  首先咱們考慮兩個被有向邊連接的點$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 }
codeforces804F

十二,總結

  其實還有不少很棒的題目沒有來得及寫詳細的題解

  可是……接下來還要去鹽焗其餘知識,沒有時間了,就涼涼了……

  在這裏作個小總結……

  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

相關文章
相關標籤/搜索