Code Festival 2017 qualA E-Modern Painting

Description

给定一个 nm 的网格。一些人站在网格的边界上。
一个人可以占据他面前的一行(列),过程是一直往前染色(每个人的颜色都不一样),直到下一个格子染上了颜色或者到达了边界。
显然不同顺序会有不同的最终状态。
求最终状态的方案数。
n,m105

Solution

好难啊,一点都不会做QAQ。
可以先假设是竖直方向的人先占据格子。(水平情况一样计算)
因为若有 L R已经先占据了格子,那么 (L,R) 的格子也一定是会被竖直方向的人占领。
那么必定是一个区间 [L,R] 中的人一起先占据了一些格子。
考虑计算这种情况下的答案,答案就是下面三种数值的乘积:

  • 考虑 [1,L1] 的网格,按不同顺序所得到的答案。
  • 考虑 [R+1,m] 的网格,按不同顺序所得到的答案。
  • 考虑 [L,R] 的网格中,位置 i 有上下两个人可以选择占据。其贡献为2k

现在只要计算出第一个值(第二个值方法类似)。
设面向右的人有 X 个,向下的Y个,向上的 Z 个。
其答案的形式一定是这样的。
这里写图片描述
把它下半部分的翻转一下就得到了一个相当于从(0,0) (Y+Z,X) 的路径方案数的东西(好大啊)
因为必须要经过翻转的那一条边,所以贡献就是

(X+Y+ZX)(X+Y+Z1X)=(X+Y+Z1X1)

然后存一下后缀前缀之类的搞一搞就好啦。
复杂度 O(n+m)

#include <bits/stdc++.h>
using namespace std;

const int N = 301010;
const int MOD = 998244353;
const int INV = (MOD + 1) >> 1;
typedef long long ll;

char S[N];
int fac[N << 2], inv[N << 2];
int v[N][2], h[N][2];
int pre[N], suf[N], p2[N];
int n, m, lim, ans;

inline void Mod(int &x) {
    while (x >= MOD) x -= MOD;
}
inline int Pow(int a, int b) {
    int c = 1;
    while (b) {
        if (b & 1) c = (ll)c * a % MOD;
        b >>= 1; a = (ll)a * a % MOD;
    }
    return c;
}
inline int Inv(int x) {
    return Pow(x, MOD - 2);
}
inline int C(int n, int m) {
    return (ll)fac[n] * inv[m] % MOD * inv[n - m] % MOD;
}
inline int Calc(int X, int Y, int Z) {
    if (X == 0) return (Y + Z) ? 0: 1;
    return C(X + Y + Z - 1, X - 1);
}
inline void Solve(void) {
    static int x, y, z;
    memset(p2, 0, sizeof p2);
    memset(suf, 0, sizeof suf);
    x = y = z = 0;
    for (int i = 1; i <= n; i++) x += h[i][1];
    p2[0] = 1;
    for (int i = 1; i <= m; i++)
        Mod(p2[i] = p2[i - 1] << (v[i][0] & v[i][1]));
    for (int i = m; i; i--) {
        if (!v[i][0] && !v[i][1]) {
            suf[i] = suf[i + 1];
            continue;
        }
        Mod(suf[i] = suf[i + 1] + (ll)Calc(x, y, z) * p2[i] % MOD);
        y += v[i][0]; z += v[i][1];
    }
    x = y = z = 0;
    for (int i = 1; i <= n; i++) x += h[i][0];
    for (int i = 1; i <= m; i++) {
        if (!v[i][0] && !v[i][1]) continue;
        pre[i] = (ll)Calc(x, y, z) * Inv(p2[i - 1]) % MOD;
        Mod(ans += (ll)pre[i] * suf[i] % MOD);
        y += v[i][0]; z += v[i][1];
    }
}


int main(void) {
    freopen("1.in", "r", stdin);
    scanf("%d%d\n", &n, &m);
    lim = max(n, m) << 2;
    inv[1] = 1;
    for (int i = 2; i <= lim; i++)
        inv[i] = (ll)(MOD - MOD / i) * inv[MOD % i] % MOD;
    fac[0] = inv[0] = 1;
    for (int i = 1; i <= lim; i++) {
        fac[i] = (ll)fac[i - 1] * i % MOD;
        inv[i] = (ll)inv[i - 1] * inv[i] % MOD;
    }
    scanf("%s\n", S);
    for (int i = 1; i <= n; i++) h[i][0] = S[i - 1] - '0';
    scanf("%s\n", S);
    for (int i = 1; i <= n; i++) h[i][1] = S[i - 1] - '0';
    scanf("%s\n", S);
    for (int i = 1; i <= m; i++) v[i][0] = S[i - 1] - '0';
    scanf("%s\n", S);
    for (int i = 1; i <= m; i++) v[i][1] = S[i - 1] - '0';
    Solve(); lim >>= 2;
    for (int i = 1; i <= lim; i++) {
        swap(h[i][0], v[i][0]);
        swap(h[i][1], v[i][1]);
    }
    swap(n, m); Solve();
    cout << max(ans, 1) << endl;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值