清北学堂模拟赛d4t6 c

分析:这道题比较有难度.

      观察题目,发现只有当一行翻了奇数次后才会产生黑色格子,设有x行被翻了奇数次,y列被翻了偶数次,那么x*m + y*n - 2*x*y = s,接下来就要解方程了.对于二元一次方程,先枚举其中一个未知数x,就能推得y = (s - x*m)/(n - 2*x).假设翻了奇数次的x行y列各只用x,y次操作,那么接下来的任务就是把剩下的没用完的次数给分配出去,而且不能改变奇偶性.如果每一次操作是把一行或一列翻两次,那么就是要把(r - x)/2次操作分给n行,(c - y)/2次操作分给m列,这个的方案数可以用隔板法来求解,即:

C((r - x) / 2 + n - 1,n - 1),C((c - y) / 2 + m - 1,m - 1),n行m列中选x行y列的方案数为C(n,x),

C(m,y),那么答案就是C((r - x) / 2 + n - 1,n - 1)*C((c - y) / 2 + m - 1,m - 1)*C(n,x)*C(m,y).

      统计方案数的时候要看y是不是整数,并且r-x和c-y要能被2整除.最后要特判一种情况:x*2=n,在这种情况下,无论多少列被染了奇数次黑色格子永远是那么多,把所有列的情况算一次就好了.

以后取模运算要单独开一个函数写,这样不容易错.

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

const int mod = 1e9 + 7, maxn = 100010;

using namespace std;
long long p[maxn];
int n, m, r, c;
long long s, v1[maxn], v2[maxn], v3[maxn], v4[maxn], ans;

long long mul(long long a, long long b)
{
    a *= b;
    if (a >= mod)
        a %= mod;
    return a;
}

void inc(long long &a, long long b)
{
    a += b;
    if (a >= mod)
        a -= mod;
}

long long qpow(long long a, long long b)
{
    long long res = 1;
    while (b)
    {
        if (b & 1)
            res = mul(res, a);
        b >>= 1;
        a = mul(a, a);
    }
    return res;
}

int main()
{
    scanf("%d%d%d%d%lld", &n, &m, &r, &c, &s);
    
    p[1] = 1;
    for (int i = 2; i <= 100000; i++) //逆元
        p[i] = ((mod - mod / i) * p[mod % i]) % mod;
        
    long long temp = 1;
    for (int i = 0; i <= r; i++)  //求C(n + i - 1,n - 1)
    {
        v1[i] = temp;
        temp = mul(temp, mul(i + n, p[i + 1]));
    }
    temp = 1;
    for (int i = 0; i <= c; i++)
    {
        v2[i] = temp;
        temp = mul(temp, mul(i + m, p[i + 1]));
    }
    temp = 1;
    for (int i = 0; i <= n; i++)
    {
        v3[i] = temp;
        temp = mul(temp, mul(n - i, p[i + 1]));
    }
    temp = 1;
    for (int i = 0; i <= m; i++)
    {
        v4[i] = temp;
        temp = mul(temp, mul(m - i, p[i + 1]));
    }
    for (long long i = r & 1; i <= min(n, r); i += 2)
    {

        if (i * 2 != n)
        {
            if (((s - (long long)m * i)) % (n - i * 2))
                continue;
            long long b = (s - (long long)i * m) / (n - i * 2);
            if (b > c || b < 0 || (c - b) & 1)
                continue;
            long long temp = v3[i];
            temp = mul(temp, v1[(r - i) >> 1]);
            temp = mul(temp, v4[b]);
            temp = mul(temp, v2[(c - b) >> 1]);
            inc(ans, temp);
        }
        else
        {
            if ((long long)i * m != s)
                continue;
            long long temp = v3[i];
            temp = mul(temp, v1[(r - i) >> 1]);
            long long cnt = 0;
            for (int b = (c & 1); b <= min(r, c); b += 2)
                inc(cnt, mul(v4[b], v2[(c - b) >> 1]));
            inc(ans, mul(temp, cnt));
        }
    }
    printf("%lld\n", ans);

    return 0;
}

 

posted @ 2017-10-10 15:34  zbtrs  阅读(200)  评论(0编辑  收藏  举报