思维题练习专场-DP篇(附题表)

转载请注明原文地址http://www.cnblogs.com/LadyLex/p/8536399.html 

听说今年省选很可怕?刷题刷题刷题

省选已经结束了但是我们要继续刷题刷题刷题

目标是“有思维难度的DP题”!

一,uoj316

  这个不用多说……NOI2017的D1T3,难度肯定是有的

  个人觉得那个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,能想到它在干什么,但是……

  那个式子,想推对必须非常严谨才行……

 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

  二进制的数位DP

  听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

posted @ 2018-03-13 05:55  LadyLex  阅读(1227)  评论(4编辑  收藏  举报