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
",res);

    return 0;
}

原文地址:https://www.cnblogs.com/JohnRan/p/13573025.html