1. 程式人生 > 實用技巧 >Educational Codeforces Round 94 (Rated for Div. 2) G

Educational Codeforces Round 94 (Rated for Div. 2) G

Mercenaries

題意:給你n個元素,每一個元素都有一個下界和上界\([li,ri]\),表示自己所在的集合大小必須在這個範圍內。再給你m\((m<=20)\)個限制條件,每一個限制由兩個元素編號組成,表示這兩個元素不能同時出現在一個集合裡。問有多少種組成合法集合的方式。

分析:首先考慮列舉集合的大小,這個時候能選的元素就是確定的。但是會存在一些不合法的情況,就是這m個限制有可能不滿足。這個時候,我們可以把有限制的點和沒有限制的點分開考慮。因為最多會有40個點會存在限制,並且如果要合法,這些有限制的點一定是可選的點中的一個獨立集。我們可以直接考慮容斥出合法的情況。考慮到集合的大小是變化的,受限的點也是變化的,但是由於點不多,所以我們可以在可選的受限的點發生變化時,直接暴力容斥出新的合法情況就可以了。

#include<bits/stdc++.h>

using namespace std;

const int N = 300005;

const int MOD = 998244353;

vector<int> add[N], del[N];

typedef long long LL;

int fac[N], inv[N];

int l[N], r[N], can[N], n, m;

int a[N], b[N], id[N], in[N];

int cnt0, cnt1;

void init(){
    fac[0]=fac[1]=inv[1]=inv[0]=1;
    for(int i=2;i<=300000;++i)fac[i]=1ll*fac[i-1]*i%MOD;
    for(int i=2;i<=300000;++i)inv[i]=1ll*(MOD-MOD/i)*inv[MOD%i]%MOD;
    for(int i=2;i<=300000;++i)inv[i]=1ll*inv[i-1]*inv[i]%MOD;
}

int C(int n, int m){
    if(m>n||m<0)return 0;
    return 1ll*fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}

/*
    只在受限點變化時暴力容斥,worst case:40*(1<<20),可以AC
*/

void dfs(int x, LL state, int coef){
    if(x==m+1){
        int t=__builtin_popcountll(state);
        for(int j=0;j<=m;++j){
            can[j]=(1ll*can[j]+1ll*coef*C(cnt1-t,j-t)%MOD)%MOD;
        }
        return;
    }

    if(in[a[x]]&&in[b[x]]){
        dfs(x+1,state|(1ll<<id[a[x]]-1)|(1ll<<id[b[x]]-1),MOD-coef);
    }

    dfs(x+1,state,coef);
}

int main(){
    init();

    scanf("%d%d", &n,&m);

    for(int i=1;i<=n;++i){
        scanf("%d%d",l+i,r+i);
        id[i]=-1;
    }

    int idx=0;
    for(int i=1;i<=m;++i){
        scanf("%d%d",a+i,b+i);
        if(id[a[i]]==-1){
            id[a[i]]=++idx;
        }
        if(id[b[i]]==-1){
            id[b[i]]=++idx;
        }
    }

    for(int i=1;i<=n;++i){
        add[l[i]].push_back(i);
        del[r[i]].push_back(i);
    }

    int res=0; LL mask=0;

    can[0]=1;
    for(int i=1;i<=n;++i){
        for(auto& x:add[i]){
            in[x]=1;
            if(id[x]==-1){
                ++cnt0;
            }else{
                ++cnt1;
            }
        }

        LL nMask=0;
        for(int j=1;j<=m;++j){
            if(in[a[j]]!=0){
                nMask|=1ll<<id[a[j]]-1;
            }
            if(in[b[j]]!=0){
                nMask|=1ll<<id[b[j]]-1;
            }
        }

        if(mask!=nMask){
            mask=nMask;
            for(int j=0;j<=m;++j)can[j]=0;
            dfs(1,0,1);
        }

        for(int j=0;j<=m;++j){
            res=(1ll*res+1ll*can[j]*C(cnt0,i-j))%MOD;
        }

        for(auto& x:del[i]){
            in[x]=0;
            if(id[x]==-1){
                --cnt0;
            }else{
                --cnt1;
            }
        }
    }

    res%=MOD;
    if(res<0)res+=MOD;

    printf("%d\n",res);

    return 0;
}