题意:给你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;
}