zoukankan      html  css  js  c++  java
  • [组合数][枚举] 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 }
  • 相关阅读:
    Python模块之pysnooper
    本站页脚HTML回顶部代码
    本站CSS代码
    Linux使用 tar命令-g参数进行增量+差异备份、还原文件
    mysql定时备份shell脚本
    Linux系统备份与还原
    MYSQL备份与恢复
    技术普及帖:你刚才在淘宝上买了一件东西
    Linux运维工程师前景
    Linux运维工程师需掌握的技能
  • 原文地址:https://www.cnblogs.com/Comfortable/p/9291405.html
Copyright © 2011-2022 走看看