组合数学+ntt+启发式合并+容斥原理

#include
#include
#include
#include

using namespace std;
typedef long long ll;
const int maxn=3e5+10, mod = 998244353, G = 3, Gi = 332748118;
typedef pair<int, int> P;
ll quick(ll a,ll b,ll m)
{
    ll ans=1;
    while(b>0)
    {
        if(b&1)
            ans=ans*a%m;
        a=a*a%m;
        b>>=1;
    }
    return ans;
}

ll jiecheng[maxn];
void ini_zuhelogn()
{
    jiecheng[0]=1;
    for(int i=1;i)
    {
        jiecheng[i]=jiecheng[i-1]*i%mod;
    }
}
ll zuhe_logn(int m,int n)
{
    if(jiecheng[0]!=1)
        ini_zuhelogn();
    ll ans=((jiecheng[m]*quick(jiecheng[n], mod-2, mod))%mod)*quick(jiecheng[m-n], mod-2, mod)%mod;
    return ans;
}

ll result_len,result[maxn],level=0,trans[maxn];

inline void NTT(ll *A, int type) {
    for(int i = 0; i < result_len; i++)
        if(i < trans[i]) swap(A[i], A[trans[i]]);
    for(int mid = 1; mid < result_len; mid <<= 1) {
        ll Wn = quick( type == 1 ? G : Gi , (mod - 1) / (mid << 1),mod);
        for(int j = 0; j < result_len; j += (mid << 1)) {
            ll w = 1;
            for(int k = 0; k < mid; k++, w = (w * Wn) % mod) {
                 int x = A[j + k], y = w * A[j + k + mid] % mod;
                 A[j + k] = (x + y) % mod,
                 A[j + k + mid] = (x - y + mod) % mod;
            }
        }
    }
}
void ntt(ll *a,int a_len,ll *b,int b_len)
{
    result_len=1;level=0;
    while(result_len<=a_len+b_len)
    {
        result_len<<=1;
        level++;
    }
    for(int i=a_len;i)
        a[i]=0;
    for(int i=b_len;i)
        b[i]=0;
    for(int i=0;i)
        trans[i]=(trans[i>>1]>>1)|((i&1)<<(level-1));
    NTT(a, 1);
    NTT(b, 1);
    for(int i=0;i)
        result[i]=(a[i]*b[i])%mod;
    NTT(result,-1);
    ll inv=quick(result_len, mod-2, mod);
    for(int i=0;i)
        result[i]=(result[i]*inv)%mod;
  
}
P a[maxn];
vector D[maxn];
priority_queue

q; ll d1[maxn],d2[maxn]; int main() { ini_zuhelogn(); int T; cin>>T; while (T--) { int n,m=0; cin>>n; for(int i=0;i) { // cout< cin>>a[i].first>>a[i].second; m+=a[i].first; int t=min(a[i].first,a[i].second); D[i].clear(); D[i].push_back(1); for(int j=1;j<=t;j++){ D[i].push_back((zuhe_logn(a[i].first, j)*zuhe_logn(a[i].second, j))%mod*jiecheng[j]%mod); // cout< } // cout< q.push(make_pair(-t-1, i)); } while(q.size()>1) { int t1=q.top().second,len1=-q.top().first; q.pop(); int t2=q.top().second,len2=-q.top().first; q.pop(); // cout< for(int i=0;i){ d1[i]=D[t1][i]; // cout< } // cout< for(int i=0;i){ d2[i]=D[t2][i]; // cout< } // cout< ntt(d1, len1, d2, len2); D[t2].clear(); for(int i=0;i1+len2;i++){ // cout< D[t2].push_back(result[i]); } // cout< q.push(make_pair(-len1+1-len2, t2)); } int t=q.top().second,len=-q.top().first; q.pop(); int flag=1; ll ans=0; for(int i=0;i) { ans=(ans+flag*jiecheng[m-i]*D[t][i])%mod; flag*=-1; } cout<<(ans+mod)%mod<<endl; } return 0; } //1 //6 //0 2 //1 0 //1 2 //0 0 //2 2 //2 0

 

你可能感兴趣的:(组合数学+ntt+启发式合并+容斥原理)