GMOJ 6809 不难题 题解

考虑我们已经有区间([L, R]),设(f_i)为在进行若干次操作后第i个恶魔连续出现在末尾且前面没有连续出现的恶魔的方案数。

先不考虑前面有没有连续出现,再枚举前面可以连续出现的恶魔去除掉。

[f_i = frac{(sum_{jin[L, R]} p_{i, j}-1)!}{prod_{jin[L, R]}(p_{i,j}-1)!}cdot(R-L+1)! - sum_{jin S}f_jcdot frac{(sum_{xin[L, R]}p_{i,x}-p_{j,x}-1)!}{prod_{xin[L, R]}(p_{i,x}-p_{j,x}-1)!}cdot(R-L+1)! ]

其中(S)代表可以连续出现在i以前的恶魔编号集合,(p_{i,j})表示i恶魔在j井中的位置。

我们在每口井后面都增加一个n+1的恶魔,这样答案就是(frac {f_{n+1}} {(R-L+1)!})

朴素的算法是(O(n^2k^3))的,但是我们发现固定左端点,枚举右端点时,分式的分子与分母分开来容易维护。注意到随机数据,那么(S)每次期望缩小(frac 1 2),时间复杂度就变成了(O(nk(n+k)))

然而我还是要吸口氧才能过

#pragma GCC optimize("-O2")
#include <cstdio>

using namespace std;

typedef long long ll;
const int maxk=300, maxn=301, mods=1000000007;

ll fact[maxk*maxn+1], invf[maxk*maxn+1];

ll qpower(ll a, int n) {
    ll s=1;
    for (; n; n/=2) {
        if (n&1) s=s*a%mods;
        a=a*a%mods;
    }
    return s;
}

void initFact(int n) {
    fact[0] = 1;
    for (int i=1; i<=n; i++) fact[i] = fact[i-1]*i%mods;
    invf[n] = qpower(fact[n], mods-2);
    for (int i=n; i>0; i--) invf[i-1] = invf[i]*i%mods;
}

int main() {
    freopen("nothard.in", "r", stdin);
    freopen("nothard.out", "w", stdout);

    static int a[maxk+1][maxn+1], p[maxn+1][maxk+1];
    int k, n;
    scanf("%d %d", &k, &n);
    initFact(n*k);
    for (int i=1; i<=k; i++) {
        for (int j=1; j<=n; j++) {
            scanf("%d", a[i]+j);
            p[i][a[i][j]] = j;
        }
        a[i][n+1] = p[i][n+1] = n+1;
    }
    n++;

    static int cor[2][maxn+1][maxn+1];
    static ll f[maxn+1], u[maxn+1][maxn+1], v[maxn+1][maxn+1];
    ll ans=0, now=0;
    for (int i=1; i<=k; i++) {
        int pos=0;
        for (int j=1; j<=n; j++) {
            int o=a[i][j];
            cor[pos][o][0] = j-1;
            u[o][o]=j-1;
            v[o][o]=invf[j-1];
            for (int t=1; t<j; t++) {
                cor[pos][o][t] = a[i][t];
                u[o][a[i][t]] = j-t-1;
                v[o][a[i][t]] = invf[j-t-1];
            }
        }

        for (int j=i+1; j<=k; j++) {
            static int book[maxn+1];
            book[0]++;
            pos=!pos;
            for (int x=1; x<=n; x++) {
                int o=a[j][x];
                u[o][o] += x-1;
                v[o][o] = v[o][o]*invf[x-1]%mods;
                f[o] = fact[u[o][o]]*v[o][o]%mods*fact[j-i+1]%mods;

                cor[pos][o][0] = 0;
                for (int y=1; y<=cor[!pos][o][0]; y++) {
                    int z=cor[!pos][o][y];
                    if (book[z]==book[0]) {
                        cor[pos][o][++cor[pos][o][0]] = z;

                        int t=x-p[j][z]-1;
                        u[o][z] += t;
                        v[o][z] = v[o][z]*invf[t]%mods;
                        f[o] = (f[o]+mods-f[z]*fact[u[o][z]]%mods*v[o][z]%mods*fact[j-i+1]%mods)%mods;
                    }
                }
                book[o] = book[0];
            }

            ans = (ans+f[n]*invf[j-i+1]%mods)%mods;
        }
    }

    printf("%lld
", ans);

    fclose(stdin);
    fclose(stdout);
    return 0;
}

原文地址:https://www.cnblogs.com/BunnyLutts/p/13913049.html