Loading

Atcoder ARC 059 题解

D - Iroha and a Grid

题意

​ 给出一个 \(n \times m \ (1 \le n,m \le 100,000)\) 的地图,只能从下或者从右走。有一片区域禁止走入,也就是禁止走入左下角的 \(a \times b\) 的方格,求从 \((1, 1)\) 走到 \((n, m)\) 的方案数。

思路

​ 看了一看,排列组合可以计算方格图的方案数,然后我们可以把这个地图分成两部分。中间被ban掉的地图的右边框就是一列分界线,枚举一下分界线上的点,计算从 \((1, 1)\) 到该点的方案数乘从该点到 \((n, m)\) 方案数,最后加和起来就行了。

代码

#include <bits/stdc++.h>

using namespace std;
#define ll long long
const int N = 200005;
const int mod = 1000000007;
ll fac[N], inv[N];

ll ksm(ll a, ll b)
{
    ll res = 1;
    while(b)
    {
        if(b & 1)   res = 1ll * res * a % mod;
        a = 1ll * a * a % mod;
        b >>= 1;
    }
    return res;
}

void init()
{
    fac[0] = inv[0] = 1ll;
    for(int i = 1; i < N; i ++)
        fac[i] = 1ll * fac[i - 1] * i % mod;
    inv[N - 1] = ksm(fac[N - 1], mod - 2);
    for(int i = N - 2; i >= 1; i --)
        inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
}

ll C(ll a, ll b)
{
    return fac[a] * inv[b] % mod * inv[a - b] % mod;
}

signed main()
{
    init();
    ll n, m, a, b;
    cin >> n >> m >> a >> b;

    ll res = 0;
    for(int i = 1; i <= n - a; i ++)
    {
        ll t1 = C(b + i - 2, b - 1);
        ll t2 = C(n + m - b - i - 1, m - b - 1);
        res = (res + t1 * t2 % mod) % mod;
    }
    cout << res << '\n';
}


E - Iroha and Haiku

题意

​ 定义一个合法序列为:在长度为 \(n\) 的序列中,序列中的每一个数字都是 \([0,10]\) ,有一段连续和可以被分成三段,三段的和分别为 \(x, \ y, \ z\)。给定 \(n, \ x, \ y, \ z \ (1 \le n \le 40, \ 1 \le x, z \le 5, \ 1 \le y \le 7)\)。请问合法序列的方案数是多少。

思路

​ 发现正面去思考会很难去重,所以考虑取补集。因为数字都很小,又是个计数问题,于是考虑 \(DP\) 来做。我们肯定是要枚举每一位的数字,所以这是第一个维度;然后第二个维度的话,需要“状压”来记录一下状态。这里的状压和通常的二进制状压不一样。例如:1 - 1;2 - 10, 3 - 100。可以发现这里的状态表示的就是长度为数字的01串,且最高位为1。为什么是这样设计呢?因为3 + 2 = 5,那么10010也是包含着10000的,那么我们就可以用这个状态来表示和了。我们枚举一下序列第 \(i\) 位,枚举状态,枚举这位填入的数字,若不合法的话就累加一下方案数,最后用 \(10^n-\sum_{i=0}^{S} f[n][i]\) 就是答案了。

代码

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N = 42, mod = 1000000007;
ll n, x, y, z;
ll f[N][1 << 17];

int main()
{
    cin >> n >> x >> y >> z;
    ll res = 1;
    for(int i = 1; i <= n; i ++)
        res = res * 10 % mod;
    ll vaild_st = (1 << (x - 1)) | (1 << (x + y - 1)) | (1 << (x + y + z - 1));
    ll st = (1 << (x + y + z)) - 1;

    f[0][0] = 1;
    for(int i = 1; i <= n; i ++)
    {
        for(ll j = 0; j <= st; j ++)
        {
            for(ll k = 1; k <= 10; k ++)
            {
                ll t = (j << k) | (1 << (k - 1)); //在当前状态后面加上填入的数字
                t &= st; //只取最后(x + y + z)位
                if((t & vaild_st) != vaild_st)
                    f[i][t] += f[i - 1][j], f[i][t] %= mod;
            }
        }
    }

    for(int i = 0; i <= st; i ++)
        res = (res - f[n][i] + mod) % mod;
    cout << res << '\n';
}
posted @ 2023-01-11 00:39  DM11  阅读(43)  评论(0编辑  收藏  举报