01二重退背包+组合数学――cf1111d

匿名 (未验证) 提交于 2019-12-02 23:47:01

退背包进阶,还是挺难想的

/*  dp1[k]表示取到体积k的方案数  dp2[i][j][k]表示左侧必选ij的情况下,取到体积k的方案数  dp2[i][j][k]=dp1[k]-左侧不选ij的方案数 但是这样比较难搞,我们把状态转换一下,dp2[i][j][k]表示左侧不选i,j,取到k的方案数 这样要两层退背包来解决  状态前两维可以直接压缩,用ans[i][j]来保存答案,复杂度O(52*52*n)  */ #include<bits/stdc++.h> using namespace std; #define maxn 200005 #define ll long long  #define mod 1000000007 ll f[maxn],inv[maxn],invf[maxn]; void init(){     f[0]=inv[1]=invf[1]=invf[0]=1;     for(int i=2;i<maxn;i++)         inv[i]=(mod-mod/i)*inv[mod%i]%mod;     for(int i=1;i<maxn;i++){         f[i]=f[i-1]*i%mod;         invf[i]=invf[i-1]*inv[i]%mod;     } }  char s[maxn]; ll cnt[100],n,C; ll dp1[maxn],dp2[maxn],dp3[maxn],ans[100][100]; void work(){     //先处理背包     dp1[0]=1;     for(int i=0;i<52;i++)         if(cnt[i])                 for(int j=n/2;j>=cnt[i];j--)                 dp1[j]=(dp1[j]+dp1[j-cnt[i]])%mod;      for(int i=0;i<52;i++)         if(cnt[i]){             //先退去一层:没有底i个物品时的状态dp2              memset(dp2,0,sizeof dp2);             for(int k=0;k<=n/2;k++)                 if(k<cnt[i]) dp2[k]=dp1[k];                 else  dp2[k]=(dp1[k]-dp2[k-cnt[i]]+mod)%mod;             ans[i][i]=dp2[n/2];                      for(int j=0;j<52;j++)                 if(j!=i && cnt[j]){                     //再退一层背包                      memset(dp3,0,sizeof dp3);                     for(int k=0;k<=n/2;k++)                         if(k<cnt[j])dp3[k]=dp2[k];                         else dp3[k]=(dp2[k]-dp3[k-cnt[j]]+mod)%mod;                     ans[i][j]=ans[j][i]=dp3[n/2];             }         } }  int main(){     init();     scanf("%s",s);     n=strlen(s);     for(int i=0;i<n;i++){         if(s[i]<='Z' && s[i]>='A')             cnt[s[i]-'A']++;         else cnt[s[i]-'a'+26]++;     }          C=2*f[n/2]*f[n/2]%mod;     for(int i=0;i<52;i++)         C=C*invf[cnt[i]]%mod;          work();          int q;cin>>q;     while(q--){         char a,b;int c,d;         scanf("%d %d",&c,&d);         a=s[c-1],b=s[d-1];         if(a<='Z' && a>='A')             c=a-'A';         else c=a-'a'+26;         if(b<='Z' && b>='A')             d=b-'A';         else d=b-'a'+26;                  cout<<ans[c][d]*C%mod<<'\n';     } }

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!