[组合数][枚举] Jzoj P3332 棋盘游戏

Description

有一个N*M的棋盘,初始每个格子都是白色的。
行操作是指选定某一行,将这行所有格子的颜色取反(黑白互换)。
列操作是指选定某一列,将这列所有格子的颜色取反。
XX进行了R次行操作C次列操作(可能对某行或者某列操作了多次),最后棋盘上有S个黑色格子。
问有多少种不同的操作方案。两种操作方案不同,当且仅当对某行或者某列操作次数不同(也就是说与操作的顺序无关)。
方案数可能很大,输出它对10^9+7取模的结果。
 

Input

输入只有5个整数N,M,R,C,S。

Output

输出有且仅有一个整数,表示答案对10^9+7取模的结果。
 

Sample Input

2 2 2 2 4

Sample Output

4
 

Data Constraint

对于20%的数据,满足N,M,R,C≤4。
对于60%的数据,满足N,M,R,C≤1500。
对于100%的数据,满足N,M,R,C≤100000,0≤S≤N*M。

题解

  • 首先,题目告诉不同情况是在不同的操作次数决定的,与顺序无关
  • 那么可以设行进行了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 }

猜你喜欢

转载自www.cnblogs.com/Comfortable/p/9291405.html
今日推荐