poj3718 Facer's Chocolate Dream
正解:组合数+$dp$。
今天考试的题,考试的时候感觉自己有点脑残过头了。。
似乎发现了所有$1$其实都是一样的,然后不知道怎么强制每种物品只选一个。。
然后就写了一个所有物品可以选任意个的$dp$,尝试与答案找一找规律,并没有找到,看完$std$发现只要再加一个转移就能过了。。所以还是讲讲正解吧。。
首先所有$1$都是一样的,所以我们并不需要状压,直接开一个背包就行。
设$f[i][j]$表示用了$i$个物品,$1$的个数为$j$的方案数,注意这个是有序状态,即使用顺序不同方案也不同。
那么首先枚举当前这个物品让$1$的个数增加了多少,可能为$1,-1,3,-3$,这一步很容易转移。
然后我们之前的转移是枚举的任意物品,必然会算重,而算重的充要条件就是$i$与之前某一个物品是一样的。
我们先强制$i$与$i-1$是相同物品,那么我们可以用$i-2$的状态来转移到$i$,最后我们再乘一个$i-1$表示第$i-1$个物品实际上是可以插到前$i-1$个位置的任意一个的。
最后由于我们算的是排列,所以还要再除以一个$m!$。
通过这道题,我发现我还是太$naive$,见过的套路还是太少了,看来还是要深入学习各种计数的套路。。
1 #include <bits/stdc++.h> 2 #define il inline 3 #define RG register 4 #define ll long long 5 #define rhl (10007) 6 #define N (1005) 7 8 using namespace std; 9 10 int f[N][N],c[N][N],goal[N],n,m,st,fac; 11 12 il int gi(){ 13 RG int x=0,q=1; RG char ch=getchar(); 14 while ((ch<'0' || ch>'9') && ch!='-') ch=getchar(); 15 if (ch=='-') q=-1,ch=getchar(); 16 while (ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar(); 17 return q*x; 18 } 19 20 il char gc(){ 21 RG char ch=getchar(); 22 while (ch!='0' && ch!='1') ch=getchar(); 23 return ch; 24 } 25 26 il int qpow(RG int a,RG int b){ 27 RG int ans=1; 28 while (b){ 29 if (b&1) ans=ans*a%rhl; 30 if (b>>=1) a=a*a%rhl; 31 } 32 return ans; 33 } 34 35 il void work(){ 36 for (RG int i=0;i<n;++i) goal[i]=0; st=0; 37 for (RG int i=0;i<n;++i) goal[i]^=gc()=='1'; 38 for (RG int i=0;i<n;++i) goal[i]^=gc()=='1'; 39 for (RG int i=0;i<n;++i) st+=goal[i]; f[0][st]=1; 40 for (RG int i=(fac=1);i<=m;fac=fac*(i++)%rhl) 41 for (RG int j=0;j<=n;++j){ 42 f[i][j]=0; 43 if (j) f[i][j]=(1LL*c[j-1][1]*c[n-j+1][2]*f[i-1][j-1]+f[i][j])%rhl; 44 if (j<n) f[i][j]=(1LL*c[j+1][2]*c[n-j-1][1]*f[i-1][j+1]+f[i][j])%rhl; 45 if (j-3>=0) f[i][j]=(c[n-j+3][3]*f[i-1][j-3]+f[i][j])%rhl; 46 if (j+3<=n) f[i][j]=(c[j+3][3]*f[i-1][j+3]+f[i][j])%rhl; 47 if (i>1) f[i][j]=(f[i][j]-1LL*(c[n][3]-i+2)*f[i-2][j]*(i-1))%rhl; 48 } 49 printf("%d\n",(f[m][0]+rhl)*qpow(fac,rhl-2)%rhl),f[0][st]=0; return; 50 } 51 52 int main(){ 53 #ifndef ONLINE_JUDGE 54 freopen("cho.in","r",stdin); 55 freopen("cho.out","w",stdout); 56 #endif 57 c[0][0]=1; 58 for (RG int i=1;i<=1000;++i){ 59 c[i][0]=c[i][i]=1; 60 for (RG int j=1;j<i;++j){ 61 c[i][j]=c[i-1][j-1]+c[i-1][j]; 62 if (c[i][j]>=rhl) c[i][j]-=rhl; 63 } 64 } 65 while (scanf("%d%d",&n,&m)!=EOF && (n|m)) work(); 66 return 0; 67 }