• 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;
    }
    
    
  • 相关阅读:
    黑马程序员————C语言基础语法二(算数运算、赋值运算符、自增自减、sizeof、关系运算、逻辑运算、三目运算符、选择结构、循环结构)
    django启动前的安装
    React的条件渲染和列表渲染
    React学习第二天
    go语言切片
    mongodb
    Flask路由层
    Flask基础简介
    celery介绍及django使用方法
    redis的介绍及django使用redis
  • 原文地址:https://www.cnblogs.com/JohnRan/p/13573025.html
Copyright © 2020-2023  润新知