Data Constraint
对于 15% 的数据,满足 m ≤ 21。
对于另外 35% 的数据,满足 n ≤ 11。
对于 80% 的数据 (包括以上 50% 的数据),满足 n ≤ 14。
对于 100% 的数据,满足 n ≤ 17。
题解
看到数据范围,就可以连续到状态压缩。
很显然每个联通块对答案的贡献都是独立的,所以只需要知道每个联通块的连通概率就可以了。
设
f
s
f_s
fs表示s这个二进制状态的点集不连通。
那么1-
f
s
f_s
fs就表示这个点集形成联通块连通的概率。
考虑转移,
枚举一个s的子集ss,
f
s
=
∑
(
1
−
f
s
s
)
∗
集
合
s
s
与
集
合
s
的
其
他
点
都
不
连
通
个
概
率
f_s=\sum (1-f_{ss})*集合ss与集合s的其他点都不连通个概率
fs=∑(1−fss)∗集合ss与集合s的其他点都不连通个概率
为了避免重复就是,ss应该要保证一定选了s里面编号最小的点。
于是现在的关键就在于如何求这个概率。
设
g
s
g_s
gs表示s这个点集里面的边全部不连通的概率,
那么就可以用g来表示出这个概率了
g
s
/
g
s
s
/
g
s
s
补
集
g_s/g_{ss}/g_{ss补集}
gs/gss/gss补集
这个就是s其他点与ss之间全部边不连通的概率了。
最后统计答案,
枚举点集s,使得s连通,而且s不与其他非s集合的点有连边,
这个情形跟转移很类似。
code
#pragma GCC optimize(2)
#pragma G++ optimize(2)
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=23,mo=998244353,M=131073;
int n,m,x,y,z,nxt[N*N],to[N*N],v[N*N],vv[N*N],lst[N],tot;
int f[M],g[M],ny[M],_2[N],ans;
bool bz[M];
char ch;
void read(int&n)
{
for(ch=getchar();ch<'0' || ch>'9';ch=getchar());
for(n=0;'0'<=ch && ch<='9';ch=getchar())n=(n<<1)+(n<<3)+ch-48;
}
int x_(int x){return (-x)&x;}
int ksm(ll x,int y)
{
ll s=1;
for(;y;y>>=1,x=x*x%mo)
if(y&1)s=s*x%mo;
return s;
}
void ins(int x,int y,int z)
{
nxt[++tot]=lst[x];
to[tot]=y;v[tot]=z;vv[tot]=ksm(z,mo-2);
lst[x]=tot;
}
int add(int x,int y){return x+y<mo?x+y:x+y-mo;}
int main()
{
freopen("fair.in","r",stdin);
freopen("fair.out","w",stdout);
_2[0]=1;for(int i=1;i<N;i++)_2[i]=_2[i-1]<<1;
read(n);read(m);
for(int i=1;i<=m;i++)
{
read(x),read(y),read(z);
if(x^y)ins(x,y,z),ins(y,x,z);
}
g[0]=ny[0]=1;
for(int s=1;s<_2[n];s++)
{
for(int i=1;i<=n;i++)
if(s&_2[i-1])
{
g[s]=g[s^_2[i-1]];ny[s]=ny[s^_2[i-1]];
for(int j=lst[i];j;j=nxt[j])
if(s&_2[to[j]-1])g[s]=(ll)g[s]*v[j]%mo,ny[s]=(ll)ny[s]*vv[j]%mo;
break;
}
}
for(int s=0;s<_2[n];s++)
{
for(int ss=s;ss;ss=(ss-1)&s)
{
if(s==ss || (x_(s)&ss)==0 || ss==0)continue;
f[s]=(f[s]+(ll)(mo+1-f[ss])*ny[ss]%mo*ny[s^ss])%mo;
}
f[s]=(ll)f[s]*g[s]%mo;
}
for(int s=1;s<_2[n];s++)
ans=(ans+(ll)(mo+1-f[s])*g[_2[n]-1]%mo*ny[s]%mo*ny[(_2[n]-1)^s])%mo;
printf("%d\n",ans);
}