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 }

 

posted @ 2018-02-26 16:06  wfj_2048  阅读(225)  评论(0编辑  收藏  举报