题解:P11834 [省选联考 2025] 岁月
C 性质
不妨将概率计数转为方案计数,最后再乘上
所有边权相等,条件可以转化为存在一棵外向生成树,考虑所有可以成为根的节点,可以发现其组成一个 SCC,更具体的,其恰好是缩点后唯一入度为
因此我们设
首先要保证
不合法的方案数等价于缩点后有多个点,并且形成了一个 DAG,因此可以仿照 DAG 容斥的方法,每次删去入度为
设
注意除了关键边和非关键边,还有在连通块内部的边,这种边不会影响连通性,可以直接给答案
由于我们只在新合并连通块时枚举子集,因此时间复杂度不超过
代码
#include <bits/stdc++.h>
#define int long long
#define lowbit(x) (x&(-x))
using namespace std;
const int N=215,M=(1<<15)+5,mo=1e9+7;
struct edge{
int u,v;
};
int c,T,n,m,U,ans;
int p[N],ed[N],fa[N],fa2[N],msk[N];
int sum[M],f[M],g[M],dp[M],ent[M],ext[M],cof[M],con[M];
vector<edge>e[N];
int read(){
int k=0;
char ch=getchar();
while(ch<'0'||ch>'9')
ch=getchar();
while(ch>='0'&&ch<='9')
k=k*10+ch-'0',ch=getchar();
return k;
}
int qpow(int x,int y){
int res=1;
while(y){
if(y&1)
res=res*x%mo;
x=x*x%mo;
y=y>>1;
}
return res;
}
int find(int x){
return fa[x]==x?x:fa[x]=find(fa[x]);
}
int find2(int x){
return fa2[x]==x?x:fa2[x]=find2(fa2[x]);
}
int ppc(int x){
return __builtin_popcountll(x);
}
int ctz(int x){
return __builtin_ctzll(x);
}
int qsum(int x,int y){
return sum[x^y]-sum[x]-sum[y];
}
int nsum(int x,int y){
return (qsum(x,ext[y]^y)+qsum(ext[x]^x,y)+qsum(ext[x]^x,ext[y]^y)*2);
}
int ok(int x,int y){
return (ext[x]&ext[y])==0;
}
bool check(int x){
return (ent[1<<ctz(x)]&x)!=x||ext[1<<ctz(x)]==ent[x];
}
void work(){
for(int s=1;s<(1<<n);s++){
if(check(s)) continue;
int t=(ext[1<<ctz(s)]&s);
cof[s]=cof[s^t]*dp[t]%mo*p[qsum(ext[s^t],ext[t])*2]%mo;
if(ext[s]==s){
t=ext[1<<ctz(s)];
if(s==t){
con[s]=0;
for(int t=s;t;t=((t-1)&s))
con[s]=(con[s]+cof[t])%mo;
}
else
con[s]=con[s^t]*con[t]%mo*p[qsum(s^t,t)*2]%mo;
}
}
for(int s=1;s<(1<<n);s++){
if(check(s)) continue;
g[s]=0,f[s]=cof[s];
for(int t=((s-1)&s);t;t=((t-1)&s))
if(ok(t,s^t)&&lowbit(t)==lowbit(s))
g[s]=(g[s]-f[t]*g[s^t]%mo*p[nsum(t,s^t)])%mo;
for(int t=s;t;t=((t-1)&s))
if(ok(t,s^t))
f[s]=(f[s]-g[t]*cof[s^t]%mo*p[qsum(ext[t]^t,ext[s^t])+qsum(ext[t],ext[s^t])])%mo;
g[s]=(g[s]+f[s])%mo;
}
for(int s=1;s<(1<<n);s++){
if(check(s)) continue;
U=ent[s],dp[s]=0;
for(int t=s^U;;t=((t-1)&(s^U))){
if(ok(s,t)&&ok(s,U^ext[s^t]))
dp[s]=(dp[s]-f[s]*g[t]%mo*con[U^ext[s^t]]%mo*p[nsum(s,t)+qsum(ext[s^t]^s^t,U^ext[s^t])+qsum(ext[s^t],U^ext[s^t])])%mo;
if(!t) break;
}
}
}
void solve(){
int u,v,w;
n=read(),m=read();
for(int i=1;i<=m;i++){
u=read(),v=read(),w=read();
e[w].push_back((edge){u-1,v-1});
}
dp[0]=1,g[0]=-1,cof[0]=1,con[0]=1;
for(int s=1;s<(1<<n);s++)
ent[s]=s,dp[s]=0,cof[s]=0,con[s]=0;
for(int i=0;i<n;i++){
fa[i]=i,fa2[i]=i;
msk[i]=(1<<i),dp[1<<i]=1,cof[1<<i]=1,con[1<<i]=1;
}
int res=1;
for(int l=1;l<=m;l++){
int stt=0;
for(edge i:e[l]){
u=find(i.u),v=find(i.v);
if(u==v)
res=res*4%mo;
else{
stt=1;
ed[i.u]|=(1<<i.v),ed[i.v]|=(1<<i.u);
u=find2(i.u),v=find2(i.v);
if(u!=v){
fa2[v]=u;
msk[u]|=msk[v];
}
}
}
if(!stt) continue;
for(int s=1;s<(1<<n);s++){
int t=ctz(s);
sum[s]=sum[s^(1<<t)]+ppc(ed[t]&s);
}
for(int s=1;s<(1<<n);s++){
ext[s]=ent[s],ent[s]=0;
for(int i=0;i<n;i++)
if(s&(1<<i))
ent[s]|=(msk[find2(i)]);
}
work();
for(int i=0;i<n;i++)
fa[i]=fa2[i],ed[i]=0;
}
ans=0;
for(int s=1;s<(1<<n);s++)
ans=(ans+res*dp[s])%mo;
ans=ans*qpow(p[2*m],mo-2)%mo;
ans=(ans+mo)%mo;
printf("%lld\n",ans);
for(int i=1;i<=m;i++)
e[i].clear();
}
signed main(){
c=read(),T=read();
p[0]=1;
for(int i=1;i<=210;i++)
p[i]=p[i-1]*2%mo;
while(T--)
solve();
return 0;
}