[ABC306] Ex - Balance Scale
题面
题目描述
给定 \(n\) 个砝码,有 \(m\) 次比较,每次会比较第 \(a_i\) 个和第 \(b_i\) 个,比较的结果构成一个长度为 \(m\) 的 </>/= 串,求有多少种串可以被生成。
数据范围
-
\(1\leq a_i\lt b_i\leq n\leq 17\)。
-
\(1\leq m\leq \frac {n(n-1)}2\)。
题解
首先先考虑没有 = 的情况。
发现一个串的是合法的等价于该串代表的偏序关系合法,即为一个 DAG,所以问题转化为给一个无向图定向,求不同有向图个数。
设 \(f_S\) 表示 \(S\) 在原图 \(G\) 的导出子图定向后不同的 DAG 个数,考虑枚举下一层的点,即新导出子图中出度为 \(0\) 的点,显然枚举的点集是一个独立集。有 \(\displaystyle f_S=\sum_{T\subseteq S,E(T)=\emptyset} (-1)^{|T|+1}f_{S/T}\),容斥是因为分别先后选取 \(T\) 的两个子集 \(T'\) 和 \(T/T'\) 与直接转移 \(T\) 本质相同。
再考虑有 = 的情况。
发现 = 相当于将 = 边相连的两个点缩在一起,于是转移时枚举的点集若不是独立集,可通过 = 边缩成独立集,故有 \(\displaystyle f_S=\sum_{T\subseteq S} (-1)^{\mathrm{comp}(T)+1}f_{S/T}\)。
时间复杂度 \(O(3^n)\) 瓶颈在于枚举子集。
#include<bits/stdc++.h>
using namespace std;
const int N=17+9;
const int M=17*17+9;
const int S=(1<<17)+9;
const int mod=998244353;
inline void AddAs(int &x,int y){if((x+=y)>=mod) x-=mod;}
inline void SubAs(int &x,int y){if((x-=y)<0) x+=mod;}
inline void MulAs(int &x,int y){x=1ll*x*y%mod;}
inline int Add(int x,int y){if((x+=y)>=mod) x-=mod;return x;}
inline int Sub(int x,int y){if((x-=y)<0) x+=mod;return x;}
inline int Mul(int x,int y){return 1ll*x*y%mod;}
int u[M],v[M],n,m;
int fa[N],comp[S],f[S];
inline void Init(){for(int i=1;i<=n;i++) fa[i]=i;}
inline int Find(int x){return fa[x]==x?x:fa[x]=Find(fa[x]);}
inline void Merge(int x,int y){fa[Find(y)]=Find(x);}
signed main(){
cin>>n>>m;
for(int i=1;i<=m;i++) cin>>u[i]>>v[i];
for(int sta=0;sta<(1<<n);sta++){
Init();
for(int i=1;i<=m;i++){
if(~sta>>u[i]-1&1) continue ;
if(~sta>>v[i]-1&1) continue ;
Merge(u[i],v[i]);
}
for(int i=1;i<=n;i++){
if(~sta>>i-1&1) continue ;
if(fa[i]!=i) continue ;
comp[sta]++;
}
}
f[0]=1;
for(int sta=1;sta<(1<<n);sta++){
for(int tta=sta;tta;tta=tta-1&sta){
if(comp[tta]&1) AddAs(f[sta],f[sta^tta]);
else SubAs(f[sta],f[sta^tta]);
}
}
cout<<f[(1<<n)-1]<<endl;
return 0;
}

浙公网安备 33010602011771号