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

#include<cstdio>
#include<iostream>
#include<vector>
#include<queue>

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<maxn;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<result_len;i++)
        a[i]=0;
    for(int i=b_len;i<result_len;i++)
        b[i]=0;
    for(int i=0;i<result_len;i++)
        trans[i]=(trans[i>>1]>>1)|((i&1)<<(level-1));
    NTT(a, 1);
    NTT(b, 1);
    for(int i=0;i<result_len;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_len;i++)
        result[i]=(result[i]*inv)%mod;
  
}
P a[maxn];
vector<ll> D[maxn];
priority_queue<P> 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<n;i++)
        {
            
//            cout<<i<<":"<<endl;
            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<<D[i][j]<<" ";
            }
//            cout<<endl;
            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<<t1<<" "<<len1<<": ";
            for(int i=0;i<len1;i++){
                d1[i]=D[t1][i];
//                cout<<d1[i]<<" ";
            }
//            cout<<endl<<t2<<" "<<len2<<": ";
            for(int i=0;i<len2;i++){
                d2[i]=D[t2][i];
//                cout<<d2[i]<<" ";
            }
//            cout<<endl;
            ntt(d1, len1, d2, len2);
            D[t2].clear();
            for(int i=0;i<len1-1+len2;i++){
//                cout<<result[i]<<" ";
                D[t2].push_back(result[i]);
            }
//            cout<<endl;
            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<len;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

 

posted @ 2020-06-01 15:12  Faker_fan  阅读(255)  评论(0编辑  收藏  举报