题意:
有n个士兵,你现在要选出一些士兵。
第i个士兵有L(i)和R(i),如果选出的总人数在[L,R]内,那么该士兵就可以被选中。
此外,有m对士兵相互憎恨,不能同时被选中。
问能选出多少种不同的非空子集,满足集合中不存在相互憎恨的士兵。
答案对998244353取模
数据范围:n<=3e5,m<=min(n*(n+1)/2,20)
解法:
正难则反,考虑计算总方案数-不合法方案数,
考虑容斥:
答案=至少0对冲突-至少一对冲突+至少两对冲突...
选人还有[L,R]的限制,要先处理一下:
令num[i]表示当我们想要选出i个人的时候,有多少个人可供选择.
如何计算出num[]:
对于一个范围在[l,r]的士兵,显然对num[l,r]有贡献,
可以利用差分,num[l]++,num[r+1]--,
最后求个前缀和就计算出num[]了.
计算容斥方案的细节在代码中注释了,
写在外面不好理解.
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=3e5+5;
const int mod=998244353;
int fac[maxm],inv[maxm];
int l[maxm],r[maxm];
pair<int,int>p[25];
int sum[maxm][45];//因为m最大20,因此第二维最大40
int num[maxm];
int n,m;
int ppow(int a,int b,int mod){
int ans=1%mod;a%=mod;
while(b){
if(b&1)ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
void init(){
fac[0]=1;
for(int i=1;i<maxm;i++)fac[i]=fac[i-1]*i%mod;
inv[maxm-1]=ppow(fac[maxm-1],mod-2,mod);
for(int i=maxm-2;i>=0;i--)inv[i]=inv[i+1]*(i+1)%mod;
}
int cal(int x){
int ans=0;
while(x){
ans++;
x&=(x-1);
}
return ans;
}
int C(int n,int m){
if(m<0||m>n)return 0;
return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
signed main(){
ios::sync_with_stdio(0);
init();
//input
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>l[i]>>r[i];
}
for(int i=0;i<m;i++){
cin>>p[i].first>>p[i].second;
}
//计算num[]
for(int i=1;i<=n;i++){
num[l[i]]++;
num[r[i]+1]--;
}
for(int i=1;i<=n;i++){
num[i]+=num[i-1];
}
//预处理sum[][]
for(int i=1;i<=n;i++){
for(int j=0;j<=m*2;j++){
sum[i][j]=(sum[i-1][j]+C(num[i]-j,i-j))%mod;
}
}
//
int ans=0;
for(int i=0;i<(1<<m);i++){
set<int>s;
for(int j=0;j<m;j++){
if(i>>j&1){//第j个冲突被选中
s.insert(p[j].first);//将冲突涉及的人加入集合.
s.insert(p[j].second);
}
}
int tot=s.size();//冲突涉及的人数
//为了使选中的冲突都生效,求它们范围的交集
int L=1,R=n;
for(auto x:s){
L=max(L,l[x]);
R=min(R,r[x]);
}
if(L>R)continue;//无法满足,跳过
//
//人数在[L,R]内,至少选择这tot个人的方案数
//在[L,R]内枚举选择的人数x,那么方案数为C(num[x]-tot,x-tot)
//需要计算C(num[L]-tot,L-tot),C(num[L+1]-tot,L+1-tot)...C(num[R]-tot,R-tot)
//可以令sum[x][y]表示C(num[x]-y,x-y)的前缀和,这个可以预处理.
//这样就可以快速计算上面的式子了.
int t=(sum[R][tot]-sum[L-1][tot]+mod)%mod;
if(cal(i)&1){
ans=(ans-t+mod)%mod;
}else{
ans=(ans+t)%mod;
}
}
cout<<ans<<endl;
return 0;
}