[组合数][枚举] Jzoj P3332 棋盘游戏
题解
- 首先,题目告诉不同情况是在不同的操作次数决定的,与顺序无关
- 那么可以设行进行了i次有效操作,列进行了j次有效操作
- 那么容易得出n*i+m*j-2*i*j=s
- 移项得:s-n*i=m*j-2*i*j
- 合并同类项:s-n*i=(m-2*i)*j
- 系数化为1:j=(s-n*i)/(m-2*i)
- 然后,在观察一下这个式子
- 发现,对于m-2*i也就是分母
- 那么如果分母等于0,在除法运算中是无解的
- 如果n*i也等于m时
- 其实时也是有解滴
- 这样的话就可以分类讨论了:
- ①如果m-2*i≠0,且s-n*i可以整除m-2*i
- 那么就可以解出j的值
- ②如果2*i=m,且2*i=n
- 那么就可以在min(m,c)中枚举j的值
- 现在就差如果求一个i,j对答案的贡献:
- 发现,对于n-i和m-j必定是偶数
- 不然的话,可以多几次有效操作
- 在n行或列分配i个有效操作的方案数是C(n,i)
- 在考虑一下剩下的无效操作
- 相当于有n个物品m个箱子,箱子是不同的,物品是相同的,把物品放进去
- 箱子里没有物品,方法数等于C(n+m-1,m-1)
- 那么一对i,j对于答案的贡献就是:C(n,i)*C(m,j)*C((n-i)/2+n-1,n-1)*C((m-j)/2+m-1,m-1)
代码
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<cstring> 5 using namespace std; 6 const long long mo=1000000007; 7 const long long N=200010; 8 long long n,m,r,c,s,ans,a[200010],b[200010]; 9 long long C(int x,int y) { return a[x]*b[y]%mo*b[x-y]%mo; } 10 long long C1(int x,int y) { return !y?1:C(x+y-1,y-1); } 11 void calc(int i,int j) { if (((r-i)%2==0)&&((c-j)%2==0)) ans=(ans+C(n,i)*C(m,j)%mo*C1((r-i)/2,n)%mo*C1((c-j)/2,m)%mo)%mo; } 12 int main() 13 { 14 scanf("%lld%lld%lld%lld%lld",&n,&m,&r,&c,&s); 15 a[0]=b[0]=b[1]=1; 16 for (int i=1;i<N;i++) a[i]=a[i-1]*i%mo; 17 for (int i=2;i<N;i++) b[i]=(mo-mo/i)*b[mo%i]%mo; 18 for (int i=2;i<N;i++) b[i]=(b[i]*b[i-1])%mo; 19 for (long long i=0;i<=min(r,n);i++) 20 if (i*2==n) 21 { 22 if (i*m==s) for (long long j=0;j<=min(c,m);j++) calc(i,j); 23 } 24 else 25 if ((s-i*m)%(n-2*i)==0) 26 { 27 long long j=(s-i*m)/(n-2*i); 28 if (j>=0&&j<=min(c,m)) calc(i,j); 29 } 30 printf("%lld",ans); 31 return 0; 32 }