一道喵题

——大佬出的隐藏得很好的组合数问题

章鱼

Time Limit: 2s
Memory Limit: 512MB

Description (看题看到不耐烦的请略过题面)

众所周知,雪舞喵有许多猫。

由于最近的天气十分炎热,雪舞城的大魔法师雪月月根本不想出门,只想宅在家里打隔膜。

大魔法师雪月月最近沉迷一个叫做社保2的隔膜,在这个隔膜的休闲对战模式中,雪月月和其他3个随机的玩家组队,与另外4个随机的玩家组成的队伍进行战斗。

一场战斗中,两个队伍会被分配不同颜色的两种油漆,玩家需要操纵一个章鱼油漆工在有限的时间内往地图上刷油漆,时间结束时,地图上油漆所占面积较多的队伍将会获得胜利。

雪月月在战斗的过程中会经常打开地图以查看自己的队伍是否处于优势(如果某一时刻的地图上,雪月月队伍的油漆所占面积多于敌方队伍的油漆所占面积,则雪月月认为自己的队伍处于优势),
以调整她下一步的战斗策略。

然而雪月月的视力并不好,在大多数情况下她不能在短时间内分辨出己方是否处于优势,而在看地图时,地图会覆盖整个屏幕,雪月月不能及时发现敌人,就会被敌人消灭,因此雪月月经常输掉对战。

虽然休闲模式的胜负并不会影响雪月月X等级的排位积分,但是输掉对战总是让猫很恼火,于是雪月月找来了雪舞喵的小猫雪萌萌,要求雪萌萌帮助她看地图,如果有雪萌萌帮她看地图她还输了,她就会拿雪萌萌来测试她最近发明的奇怪魔法。

这样雪月月需要看地图的时候,只需打开地图0.1秒,让雪萌萌在1秒内告诉她她的队伍是否处于优势,这样就能方便的调整战斗策略而不会在看地图时被敌人偷袭了。

然而只会卖萌的雪萌萌并不能在0.1秒内看清如此复杂的地图,更不能在1秒内算出雪月月的队伍是否处于优势。

为了解决第一个问题,雪萌萌将地图划分成了\(n \times m\)的网格(每个格子的面积相等),并在0.1秒内记住了网格中每个格子的大致情况。

但就算是把地图划分成了网格,雪萌萌依然不能在1秒内算出答案,但是雪萌萌又相当不想被拿来测试奇怪的魔法,于是雪萌萌找到了你,请你在1秒内告诉她,雪月月的队伍有多大概率处于优势。

Input

Read from standard input.

输入包含多组数据,每组数据的第一行包含由1个空格分隔的两个正整数\(n\), \(m\),代表雪萌萌将地图分为\(n\)\(m\)列的网格。

接下来的\(n\)行,每行有\(m\)个字符代表雪萌萌记下的每个格子的大致情况。

输入数据以一组\(n=0,m=0\)的不合法数据结束,你不必给出这组数据的答案。

字符代表的含义如下:

  • 0表示这一格里没有任何油漆
  • +表示这一格里充满了雪月月队伍的油漆
  • -表示这一格里充满了敌方队伍的油漆
  • *表示这一格里既有雪月月队伍的油漆,又有敌方队伍的油漆

根据数格子的国际惯例,对于同时存在双方队伍油漆的格子,若雪月月队伍的油漆所占面积大于等于格子的一半,则可以认为这个格子充满雪月月队伍的油漆,否则可以认为这个格子充满敌方队伍的油漆。

然而不幸的是,雪萌萌并没有记住这样的格子里雪月月队伍的油漆所占面积是否大于等于格子的一半,于是雪萌萌认为每个这样的格子中,雪月月队伍的油漆所占面积大于等于格子一半的概率是\(\frac{1}{2}\),小于格子一半的概率也是\(\frac{1}{2}\),且格子之间互不影响。

输入数据巨大,请使用较快速的读入方式

Output

Print to standard output.

对于每组数据,输出一行一个整数。

设雪月月获胜的概率为\(p\),可以证明\(p \times 2^{nm}\)是一个整数,请你输出其对\(10^9+7\)取模后的结果。

Sample 1

Input

2 2
0+
-0
5 5
0+-+0

+0-0+

++-++

*****

1 1
+
3 3
+-+
*-*
+*+
0 0

Output

0
16777216
2
448

Sample 2

见样例数据下载中ex_splat2.inex_splat2.out

Restrictions

对于\(20\%\)的数据,保证\(n \times m \le 5000\)

对于另\(5\%\)的数据,保证输入数据中不包含+-*

对于另\(5\%\)的数据,保证输入数据中不包含*

对于\(100\%\)的数据,保证\(n,m \le 5000\)\(\sum n, \sum m \le 10^4\)

题面解释

有一个 \(n*m\) 的矩阵,里面是+-*0四个字符,*有 \(\frac{1}{2}\) 的概率会变成+或- , 让你计算+的个数大于-的概率乘上 $ 2^{nm}$ 的结果。结果可以证明一定是整数。

解析来了

让我们来推导一下。设+-*的个数分别是x,y,z,这样如果+和-各占一半,那就要把他们的个数加起来整除2,也就是$ (x+y+z) \div 2$ 。然鹅+的个数要比-多,那原式再加1呗。\((x+y+z) \div 2 + 1\) 是+要达到的个数,他被z减的得数就是+至少在* 里取的个数。也就是说从 \(z\) 中取至少$ z - (x+y+z) \div2 - 1$ 个*,注意,是至少。也就是说在求组合数的同时,还要求组合数的后缀和。
那现在我们知道了要求组合数,可以看到数据有点大,杨辉三角已经会炸了,我们用线性求逆元来预处理组合数。这个方法就不在这里多说了。

我的代码(我的跑得慢一点……)

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int read()
{
    int x = 0,t = 1;
    char c = getchar();
    while((c > '9' || c < '0') && c != '-')
        c = getchar();
    if(c == '-')
        t = -1,c = getchar();
    while(c >= '0' && c <= '9')
        x = x * 10 + c - 48,c = getchar();
    return x * t;
}


typedef long long ll;
const ll mod = 1e9+7;
const int maxn = 2.5e7 + 1;
ll ans,co,en,va,bo,fm,fz;



ll inv[maxn];
ll fac[maxn];
ll Com(ll n,ll m){
    return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
void init(){
    inv[0]=fac[0]=1;
    inv[1]=1;
    for(int i=1;i<maxn;i++){
        fac[i]=fac[i-1]*i%mod;
    }
    inv[1]=1;
    for(int i=2;i<maxn;i++){
        inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;
    }
    inv[0]=1;
    for(int i=1;i<maxn;i++){
        inv[i]=inv[i-1]*inv[i]%mod;
    }
}
ll ksm(ll a,ll b)
{
    if(b == 0)
        return 1;
    if(b == 1)
        return a % mod;
    if(b % 2 == 0 && b != 0)
        return ksm(a,b>>1) * ksm(a,b>>1) % mod;
    else
        return a * ksm(a,b>>1) * ksm(a,b>>1) % mod;
}

void calcu(int nn,int mm)
{
    if(co == en && bo == 0)
    {
        ans = 0;
        return;
    }
/*  if(co == en && bo > 0)
    {
        ans = ksm(2,nn*mm-1) % mod;
        return;
    }*/
    if(co > en && bo == 0)
    {
        ans = ksm(2,nn*mm) % mod;
        return;
    }
    if(co < en && bo == 0)
    {
        ans = 0;
        return;
    }
    ll sum = co + en + bo;
    sum = sum / 2 + 1;
    if(sum - co > bo)
    {
        ans = 0;
        return;
    }
    ll dd = bo - (sum - co);
    fz = 0;
    for(int i=0;i<=dd;i++)
        fz += Com(bo,i) % mod;
    ans = (ksm(2,nn*mm-bo) % mod) * (fz % mod) % mod;
}
int main()
{
    freopen("T1.in","r",stdin);
    freopen("T1.out","w",stdout);
    init();
    while(1)
    {
        int n,m;
        co=0,en=0,va=0,bo=0;
        n = read();
        m = read();
        if(n == 0 && m == 0)
            break;
        
        for(int i=1;i<=n;i++)
        {
            char map[6005];
            scanf("%s",map+1);
            for(int j=1;j<=m;j++)
            {
                if(map[j] == '+')
                    co++;
                if(map[j] == '-')
                    en++;
                if(map[j] == '0')
                    va++;
                if(map[j] == '*')
                bo++;
                
            }
    //      printf("%lld %lld %lld\n",co,en,bo);
        }
        calcu(n,m);
        
        printf("%lld\n",ans);
        
    }
    
    return 0;
}

大佬的代码(斯特的)

#define yuki(x, y) for(int i = x, __yuki = y; i < __yuki; ++i)
#define yukj(x, y) for(int j = x, __yukj = y; j < __yukj; ++j)
#define yukii(x, y) for(int i = x, __yukii = y; i <= __yukii; ++i)
#define yukji(x, y) for(int j = x, __yukji = y; j <= __yukji; ++j)
#define yuk(x, y, z) for(int x = y, __yuk = z; x < __yuk; ++x)
#define yui(x, y, z) for(int x = y, __yui = z; x >= __yui; --x)
#define sclr(x) memset(x, 0, sizeof(x))
#define sclr1(x) memset(x, -1, sizeof(x))
#define scl(x, y) memset(x, y, sizeof(x))
#define ft first
#define sc second
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long lol;
int n, m, c, p, _;
const int maxn = 25000000, M = 1000000007;
int jc[25000100], nya[25000100], ny[maxn+100], prime[maxn/10], _2[maxn+100], tot;
char buf[5100];
inline int st(int a, int b){return (lol)a*b%M;}
inline int &ep(int &a, int b){return a = (a+b)%M;}
inline int C(int nn, int kk){return st(jc[nn], st(nya[kk], nya[nn-kk]));}
void exgcd(int a, int b, int &x, int &y)
{
    if(!b) x = 1, y = 0;
    else
    {
    exgcd(b, a%b, y, x);
    y -= a/b*x;
    }
}
void pre()
{
    ny[1] = 1;
    yukii(2, maxn)
    {
    if(!ny[i])
    {
        prime[tot++] = i;
        exgcd(i, M, ny[i], _);
        ny[i] = (ny[i]+M)%M;
    }
    for(int j = 0; j < tot && i*prime[j] <= maxn; ++j)
    {
        ny[i*prime[j]] = st(ny[i], ny[prime[j]]);
        if(!(i%prime[j])) break;
    }
    }
    jc[0] = nya[0] = _2[0] = 1;
    yukii(1, maxn)
    {
    jc[i] = st(i, jc[i-1]);
    nya[i] = st(ny[i], nya[i-1]);
    _2[i] = _2[i-1]<<1;
    _2[i] = _2[i] > M ? _2[i]-M : _2[i];
    }
}
void sread()
{
    c = 0, p = 0;
    yuki(0, n)
    {
    scanf("%s", buf);
    yukj(0, m)
        if(buf[j] == '+') ++c;
        else if(buf[j] == '-') --c;
        else if(buf[j] == '*') ++p;
    }
}
int main(int argc, char **argv)
{
    pre();
    while(scanf("%d%d", &n, &m) > 0 && n)
    {
    sread();
    if(c+p <= 0) puts("0");
    else if(c-p > 0) printf("%d\n", _2[n*m]);
    else
    {
        int ans = 0;
        yukii((p-c>>1)+1, p) ep(ans, C(p, i));
        printf("%d\n", st(ans, _2[n*m-p]));
    }
    }
    return 0;
}

请不要转载此文或拿题面出数据给其他人做……毕竟是大佬出的题,有版权的( • ̀ω•́ )✧!

猜你喜欢

转载自www.cnblogs.com/Ch-someone/p/9461452.html