• 思维题练习专场-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
    ",(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
    ",(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
    ",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
    ",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("
    ");
    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
    ",(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
    ",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
    ",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
    ",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("
    ");    
    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
    ",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

  • 相关阅读:
    【FROM】java控件重绘AWT/SWINGPainting in AWT and Swing (EN)
    linux yum使用管理详细使用
    远程控制 vc++实现
    java 鼠标事件Dragged和Moved 及java显示GIF在JLabel、JButton
    C语言字符串函数大全
    C# 训练场(四)创建系统热键,并向活动窗口输入信息
    潜移默化学会WPF(样式) DataGrid(转载)
    时间查询
    Sqlserver2012 根据数据库mdf文件生成log文件,解决无法附加mdf文件
    我知道的一些 ”运行“ 窗体下的命令,个人使用
  • 原文地址:https://www.cnblogs.com/LadyLex/p/8536399.html
Copyright © 2020-2023  润新知