洛谷 P5339 [TJOI2019]唱、跳、rap和篮球

大中锋的学院要组织学生参观博物馆,要求学生们在博物馆中排成一队进行参观。他的同学可以分为四类:一部分最喜欢唱、一部分最喜欢跳、一部分最喜欢rap,还有一部分最喜欢篮球。如果队列中\(k\),\(k + 1\),\(k + 2\),\(k + 3\)位置上的同学依次,最喜欢唱、最喜欢跳、最喜欢rap、最喜欢篮球,那么他们就会聚在一起讨论蔡徐坤。大中锋不希望这种事情发生,因为这会使得队伍显得很乱。大中锋想知道有多少种排队的方法,不会有学生聚在一起讨论蔡徐坤。两个学生队伍被认为是不同的,当且仅当两个队伍中至少有一个位置上的学生的喜好不同。由于合法的队伍可能会有很多种,种类数对\(998244353\)取模。

\(n \le 1000\)\(a, b, c, d \le 500\)


看起来非常可以容斥啊,所以直接考虑容斥,设 \(f_i\) 表示至少有 \(i\) 组人在讨论的方案数, \(g_i\) 表示恰好有 \(i\) 组人在讨论的方案数。

因为这个是无标号的,所以有容斥式子:

\[g_i=\sum_{j=i}^m(-1)^{j-i}f_j \]

\(m\) 是最多可以有几组人,那么 \(ans = g_0\)

然后计算 \(f_i\) ,我们先选出 \(i\) 组人,然后剩下的四种颜色进行排列,设 \(S(a,b,c,d,n)\) 表示四种颜色的数量,要选出来 \(n\) 个组成排列的方案,然后加上 \(i\) 组人我们有 \(n-3i\) 个位置,对于每一组人要从这里头选一个位置放上,所以这样选的方案是 \(n-3i\choose i\) ,于是就可以这么计算 \(f_i\)

\[f_i={n-3i\choose i}\times S(a-i,b-i,c-i,d-i,n-4i) \]

然后考虑如何计算 \(S\) ,我们可以对每一种颜色写出 EGF ,因为无标号,所以就是 \(\sum_{i=0}^{co}\frac{x^i}{i!}\)\(co\) 就是一种颜色的个数,然后把这四个 EGF ,做卷积就可以计算出来了。

复杂度是 \(O(n^2\log n)\)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
const int N = 2e3;
const int M = 2e4;
const int p = 998244353;
using namespace std;
int n,a,b,c,d,m,f[N + 5],fac[N + 5],inv[N + 5],ans,G[M + 5][2],rev[M + 5],maxn,lg,A[M + 5],B[M + 5];
int C(int n,int m)
{
    return 1ll * fac[n] * inv[n - m] % p * inv[m] % p;
}
int mypow(int a,int x){int s = 1;for (;x;x & 1 ? s = 1ll * s * a % p : 0,a = 1ll * a * a % p,x >>= 1);return s;}
void prework(int n)
{
    lg = 0,maxn = 1;
    while (maxn < n)
        maxn <<= 1,lg++;
    for (int i = 0;i < maxn;i++)
        rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << lg - 1);
}
void ntt(int *a,int typ)
{
    for (int i = 0;i < maxn;i++)
        if (i < rev[i])
            swap(a[i],a[rev[i]]);
    for (int i = 1;i < maxn;i <<= 1)
        for (int j = 0;j < maxn;j += i << 1)
            for (int k = 0;k < i;k++)
            {
                int x = a[j + k],t = 1ll * G[k + i][typ] * a[j + k + i] % p;
                a[j + k] = (x + t) % p;
                a[j + k + i] = (x - t) % p;
            }
    if (!typ)
    {
        int in = mypow(maxn,p - 2);
        for (int i = 0;i < maxn;i++)
            a[i] = 1ll * a[i] * in % p;
    }
}
int S(int a,int b,int c,int d,int n)
{
    prework((a + b + c + d + 4) * 2);
    for (int i = 0;i < maxn;i++)
        A[i] = B[i] = 0;
    for (int i = 0;i <= a;i++)
        A[i] = inv[i];
    for (int i = 0;i <= b;i++)
        B[i] = inv[i];
    ntt(A,1);
    ntt(B,1);
    for (int i = 0;i < maxn;i++)
        A[i] = 1ll * A[i] * B[i] % p;
    for (int i = 0;i < maxn;i++)
        B[i] = 0;
    for (int i = 0;i <= c;i++)
        B[i] = inv[i];
    ntt(B,1);
    for (int i = 0;i < maxn;i++)
        A[i] = 1ll * A[i] * B[i] % p;
    for (int i = 0;i < maxn;i++)
        B[i] = 0;
    for (int i = 0;i <= d;i++)
        B[i] = inv[i];
    ntt(B,1);
    for (int i = 0;i < maxn;i++)
        A[i] = 1ll * A[i] * B[i] % p;
    ntt(A,0);
    return 1ll * fac[n] * A[n] % p;
}
int main()
{
    cin>>n>>a>>b>>c>>d;
    m = min(min(min(min(a,b),c),d),n / 4);
    fac[0] = 1;
    for (int i = 1;i <= N;i++)
        fac[i] = 1ll * fac[i - 1] * i % p;
    inv[1] = 1;
    for (int i = 2;i <= N;i++)
        inv[i] = 1ll * inv[p % i] * (p - p / i) % p;
    inv[0] = 1;
    for (int i = 1;i <= N;i++)
        inv[i] = 1ll * inv[i - 1] * inv[i] % p;
    prework((a + b + c + d + 4) * 2);
    for (int i = 1;i < maxn;i <<= 1)
    {
        int g1 = mypow(3,(p - 1) / (i << 1)),g2 = mypow(mypow(3,p - 2),(p - 1) / (i << 1));
        G[i][0] = G[i][1] = 1;
        for (int j = 1;j < i;j++)
            G[i + j][1] = 1ll * G[i + j - 1][1] * g1 % p,G[i + j][0] = 1ll * G[i + j - 1][0] * g2 % p;
    }
    for (int i = 0;i <= m;i++)
        f[i] = 1ll * C(n - 3 * i,i) * S(a - i,b - i,c - i,d - i,n - 4 * i) % p;
    for (int i = 0;i <= m;i++)
        ans += ((i & 1) ? -1 : 1) * f[i],ans %= p;
    cout<<(ans + p) % p<<endl;
    return 0;
}
posted @ 2021-01-15 17:17  eee_hoho  阅读(116)  评论(0编辑  收藏  举报