首页 > 其他分享 >luogu P5339 [TJOI2019]唱、跳、rap和篮球 (容斥,指数型母函数,NTT)

luogu P5339 [TJOI2019]唱、跳、rap和篮球 (容斥,指数型母函数,NTT)

时间:2022-10-17 11:14:10浏览次数:86  
标签:1234 const int luogu ll 容斥 NTT num inv

https://www.luogu.com.cn/problem/P5339

要求不含1234的方案,反过来求含至少一个1234的方案。
钦定存在i个位置有1234,位置的方案是Cn-3i, i. 其他n-4i个位置的方案是多重集排列:
1 的生成函数$\sum_{i=0}^{num [1]} \frac{x^{i}}{i !}$1-4的生成函数相乘(NTT)得到cal(i),结果的n - 4i项就是其他位置的方案
钦定存在i个位置有1234的方案就是 f(i)=(Cn-3i, i)* cal(i)
钦定存在1个位置有1234的方案, 将恰好含有2个位置1234的方案重复计算了2次,减去钦定存在2个位置有1234的方案,再将钦定存在3个位置有1234的方案加回来...
答案就是:
\(f(0) - \sum_{i=1}^{m}(-1)^{i + 1}f(n)\)

#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false) ,cin.tie(0), cout.tie(0);
//#pragma GCC optimize(3,"Ofast","inline")
#define ll long long
//#define int long long
const int N = 5e2 + 5;
const int M = 1e3 + 5;
const int INF = 0x3f3f3f3f;
const ll LNF = 0x3f3f3f3f3f3f3f3f;
const int mod = 998244353;
const double PI = acos(-1.0);
const double eps = 1e-13;
const int MATN = 102;
const int p = 998244353, G = 3, Gi = 332748118;//这里的Gi是G的除法逆元

ll num[5]; int n;
ll F[M << 1];ll inv[M << 1];
ll qmi(ll m, ll k ) {
    ll res = 1;
    while(k) {
        if(k & 1) res = res *m % mod;
        m = m * m % mod;
        k >>= 1;
    }
    return res;
}
ll C(ll n, ll m ) {
    return F[n] * inv[m] % mod * inv[n - m] % mod;
}
void init(int n){
    F[0]=inv[0]= 1;
    for(int i=1;i<=n;i++)F[i]=F[i-1]*i%mod;//求阶乘
    inv[n] = qmi(F[n],mod - 2);//随便求一下n的逆元
    for(int i=n-1;i>=1;i--)inv[i]=inv[i+1]*(i+1)%mod;//由最后一个往前推
}
int limit = 1;//
int L;//二进制的位数
int RR[M << 2];
ll a[5][M << 2];
void NTT(ll *A, int type)
{
    for(int i = 0; i < limit; ++ i)
        if(i < RR[i])
            swap(A[i], A[RR[i]]);
    for(int mid = 1; mid < limit; mid <<= 1) {//原根代替单位根
        //ll wn = qpow(type == 1 ? G : Gi, (p - 1) / (mid << 1));
        ll wn = qmi(G, (p - 1) / (mid * 2));
        if(type == -1) wn = qmi(wn, p - 2); //wn的-1次方
        //如果超时了上面if这句话删掉,在下面的if(type == -1)里加上下面这个循环
        /*for (int i = 1; i < limit / 2; i ++)
        swap(A[i], A[limit - i]); */
        //逆变换则乘上逆元,因为我们算出来的公式中逆变换是(a^-ij),也就是(a^ij)的逆元
        for(int pos = 0; pos < limit; pos += mid * 2) {
            ll w = 1;
            for(int j = 0; j < mid; ++ j, w = (w * wn) % p) {
                int x = A[pos + j], y = w * A[pos + mid + j] % p;
                A[pos + j] = (x + y) % p;
                A[pos + j + mid] = (x - y + p) % p;

            }
        }
    }

    if(type == -1) {
        ll limit_inv = qmi(limit, mod - 2);//N的逆元(N是limit, 指的是2的整数幂)
        for(int i = 0; i < limit; ++ i)
            A[i] = (A[i] * limit_inv) % p;//NTT还是要除以n的,但是这里把除换成逆元了,inv就是n在模p意义下的逆元
    }
}
ll cal(int k) {
    for(limit = 1, L = 0; limit <= num[1] + num[2] + num[3] + num[4]; limit <<= 1) L ++ ;
    for(int i = 0; i < limit; ++ i) {
        RR[i] = (RR[i >> 1] >> 1) | ((i & 1) << (L - 1));
    }
    for ( int i = 1; i <= 4; ++ i ) {
        for ( int j = 0; j <= num[i] - k; ++ j ) {

            a[i][j] = inv[j];
        }
        for ( int j = num[i] - k + 1; j < limit; ++ j ) a[i][j] = 0;
    }
    for ( int i = 1; i <= 4; ++ i ) NTT(a[i], 1);
    for ( int i = 2; i <= 4; ++ i ) {
        for ( int j = 0; j < limit; ++ j ) {
            a[1][j] = a[1][j] * a[i][j] % mod;
        }
    }
    NTT(a[1], -1);
    return a[1][n - 4 * k] * F[n - 4 * k] % mod;
}
int main () {
    IOS
    init(2000);
    cin >> n;
    for ( int i = 1; i <= 4; ++ i ) cin >> num[i];
    sort(num + 1, num + 1 + 4);
    ll ans = 0;
    for ( int i = 0; i <= num[1] && i <= n / 4; ++ i ) {
        ll tmp = C(n - 3 * i, i) * cal(i) % mod;
        if(i & 1) ans = (ans -  tmp) % mod;
        else ans = (ans + tmp) % mod;
    }
    cout << (ans + mod) % mod << '\n';
    return 0;
}

标签:1234,const,int,luogu,ll,容斥,NTT,num,inv
From: https://www.cnblogs.com/muscletear/p/16798341.html

相关文章

  • 【luogu CF645E】Intellectual Inquiry(DP)(结论)(矩阵乘法)
    IntellectualInquiry题目链接:luoguCF645E题目大意给你一个序列,值域在1~k,然后要你在后面再加上m个数,也要满足值域,然后使得本质不同的子序列个数最多,输出这个数量。......
  • 【luogu P5161】WD与数列(SA)(单调栈)
    WD与数列题目链接:luoguP5161题目大意给你一个序列,问你有多少对区间,长度相同,没有相交部分,而且一个区间里面所有数同时加上某个数可以变成另一个区间。思路首先发现它......
  • luogu P3232 [HNOI2013]游走 (期望, 高斯消元)
    https://www.luogu.com.cn/problem/P3232思路:算出每条边的期望访问次数,将期望访问次数多的赋予小的编号。一条边的期望访问次数=访问点u的期望/u的度+访问点v的期望......
  • luoguP1505旅游(处理边权的树剖)
    /*luogu1505非常简单的处理边权的树剖题。在树上将一条边定向,把这条边的权值赋给这条边的出点树剖的时候不计算lca权值即可*/#include<bits/stdc......
  • Luogu1398
    思路什么毒瘤细节题。考虑如何解决。考虑对这些条件做出进一步的抽象。于是我们做出细致的观察。方便起见,我们横、纵坐标自左往右、自上往下标号。字母之间的关系O......
  • LuoguP2633 Count on a tree
    题意给定一棵\(n\)个节点的树,每个点有一个权值。有\(m\)个询问,每次给你\(u,v,k\),你需要回答\(u\text{xorlast}\)和\(v\)这两个节点间第\(k\)小的点权。其......
  • 【luogu CF1396D】Rainbow Rectangles(线段树)
    RainbowRectangles题目链接:luoguCF1396D题目大意给你一个L*L的矩形,里面有n个点,有各自的颜色。然后问你有多少个端点是整点的矩形可以圈出所有颜色至少有一个点。......
  • Luogu P3469 [POI2008]BLO-Blockade
    [P3469POI2008]BLO-Blockade-洛谷|计算机科学教育新生态(luogu.com.cn)图\(G\)本身联通。删除\(u\)的连边后会形成\(k\ge2\)个连通块(至少会把\(u\)隔离出......
  • uoj34 多项式乘法 ntt
    ​​http://www.elijahqi.win/2018/03/17/uoj34ntt/​​​这是一道模板题。给你两个多项式,请输出乘起来后的多项式。输入格式第一行两个整数nn和mm,分别表示两个多项......
  • Luogu2167 Bill的挑战 - 容斥 - dp -
    题目链接:https://www.luogu.com.cn/problem/P2167题解:摘录一段描述容斥题目的话:本题中,关于容斥系数,可以先感性理解一下,严格证明可以用即除了自身,自身的超集都计算......