题解:P11834 [省选联考 2025] 岁月
P11834
为了方便,用 +- 来表示集合的加减。
记
转为数方案数,把无向边当作两条有向边,求有多少种删边方案合法。
数合法的外向生成树
对于 C 性质,边权全相等,合法的外向生成树就是合法的最小生成树。
要存在合法的外向生成树,即存在一些根能去到所有点,那缩点后这些根应当构成一个强连通分量,且这个强连通分量是唯一的
数强连通分量即 主旋律。
设
先算
至于求答案。设
code
int n,m,ans;
int e[maxn],num[1<<maxn];
int f[1<<maxn],g[1<<maxn];
int pw[maxn*maxn];
void work(){
n=read();m=read();ans=0;
for(int i=0;i<n;i++)e[i]=0;
for(int s=0;s<(1<<n);s++)f[s]=g[s]=num[s]=0;
for(int i=1;i<=m;i++){
int u=read()-1,v=read()-1,w=read();
e[u]|=1<<v,e[v]|=1<<u;
num[(1<<u)|(1<<v)]+=2;
}
for(int i=0;i<n;i++){
for(int s=0;s<(1<<n);s++)if(s&(1<<i))num[s]+=num[s^(1<<i)];
}
f[0]=1,g[0]=mod-1;
for(int s=1;s<(1<<n);s++){
for(int t=(s-1)&s;t;t=(t-1)&s)if(__lg(s)==__lg(t))(g[s]+=mod-f[t]*g[s^t]%mod)%=mod;
f[s]=pw[num[s]];
for(int t=s;t;t=(t-1)&s){
int cnt=(num[s]-num[t]-num[s^t])/2+num[s^t];
(f[s]+=mod-g[t]*pw[cnt]%mod)%=mod;
}
(g[s]+=f[s])%=mod;
}
for(int s=1;s<(1<<n);s++){
int cnt=(2*m-num[s]-num[((1<<n)-1)^s])/2+num[((1<<n)-1)^s];
for(int t=s;t;t=(t-1)&s){
(ans+=mod-f[t]*g[s^t]%mod*pw[cnt]%mod)%=mod;
}
}
printf("%lld\n",ans*ksm(ksm(4,m))%mod);
}
数合法的最小外向生成树
要求 最小外向生成树 权值等于原图 最小生成树 权值。
对于最小的限制,就是要求对于任意一个权值
于是从小到大按边权加入边,只考虑能使原来连通块合并的新边。这些新边权值全部相同,可以把之前连通块的根当作点,求选一些新边使这些点连通的方案数。
设
对于两个不同连通块的可能的根
算
求
只有连通块合并时枚举子集,更小连通块做的次数加起来应该没有全局做的多,复杂度
code
int n,m,res,mul=1;
int e[maxn],num[1<<maxn];
int f[1<<maxn],g[1<<maxn],coef[1<<maxn];
int pw[maxn*maxn];
int fa[maxn],id[maxn],vis[maxn];
int fd(int x){
if(fa[x]==x)return x;
return fa[x]=fd(fa[x]);
}
tuple<int,int,int> edge[maxn*maxn];
int ans[1<<maxn];
int bel[1<<maxn],sum[1<<maxn];
int cross(int s,int t){return (num[s|t]-num[s]-num[t])/2;}
void work(){
n=read();m=read();mul=1;
for(int i=1;i<=m;i++){
int u=read()-1,v=read()-1,w=read();
edge[i]={w,u,v};
}
for(int s=0;s<(1<<n);s++)ans[s]=0;
for(int i=0;i<n;i++)ans[1<<i]=1;
sort(edge+1,edge+m+1);
for(int i=0;i<n;i++)fa[i]=i,id[i]=1<<i;
for(int l=1,r=1;l<=m;l=r+1){
while(r+1<=m&&get<0>(edge[l])==get<0>(edge[r+1]))r++;
bool fl=0;
for(int i=l;i<=r;i++){
auto[w,u,v]=edge[i];
if(fd(u)!=fd(v))fl=1;
else mul=mul*4%mod;
}
if(!fl)continue;
for(int i=0;i<n;i++)e[i]=0;
for(int s=0;s<(1<<n);s++)num[s]=0;
for(int i=l;i<=r;i++){
auto[w,u,v]=edge[i];
if(fd(u)!=fd(v)){
e[u]|=1<<v,e[v]|=1<<u;
num[(1<<u)|(1<<v)]+=2;
}
}
for(int i=0;i<n;i++){
for(int s=0;s<(1<<n);s++)if(s&(1<<i))num[s]+=num[s^(1<<i)];
}
for(int s=0;s<(1<<n);s++)sum[s]=0;
for(int i=0;i<n;i++)if(fa[i]==i){
for(int s=id[i];s;s=(s-1)&id[i])bel[s]=id[i],(sum[id[i]]+=ans[s])%=mod;
}
sum[0]=coef[0]=1;
for(int s=1;s<(1<<n);s++){
int u=__lg(s);
bel[s]=bel[1<<u]|bel[s^(1<<u)];
if((s&bel[1<<u])==bel[1<<u])sum[s]=sum[s^bel[1<<u]]*sum[bel[1<<u]]%mod;
else sum[s]=0;
coef[s]=coef[s^(s&bel[1<<u])]*ans[s&(bel[1<<u])]%mod;
}
for(int i=0;i<n;i++)vis[i]=0;
for(int i=l;i<=r;i++){
auto[w,u,v]=edge[i];
u=fd(u),v=fd(v);
if(u!=v)fa[u]=v,vis[v]=1,id[v]|=id[u],id[u]=0;
}
for(int i=0;i<n;i++)if(fa[i]==i&&vis[i]){
vector<int> ids;
for(int s=id[i];s;s=(s-1)&id[i])ids.pb(s);
reverse(ids.begin(),ids.end());
f[0]=1,g[0]=mod-1;
for(int s:ids){
g[s]=0;for(int t=(s-1)&s;t;t=(t-1)&s)if(!(bel[t]&bel[s^t])&&__lg(s)==__lg(t)){
int cnt=cross(t,bel[s^t]^(s^t))+cross(s^t,bel[t]^t)+num[bel[s]^s]-num[bel[t]^t]-num[bel[s^t]^(s^t)];
(g[s]+=mod-f[t]*g[s^t]%mod*pw[cnt]%mod)%=mod;
}
f[s]=coef[s]*pw[num[bel[s]]]%mod;
for(int t=s;t;t=(t-1)&s)if(!(bel[t]&bel[s^t])){
int cnt=cross(t,bel[s^t])+num[bel[s]^t]-num[bel[t]^t];
(f[s]+=mod-g[t]*coef[s^t]%mod*pw[cnt]%mod)%=mod;
}
(g[s]+=f[s])%=mod;
}
for(int s:ids)ans[s]=0;
for(int s:ids){
for(int t=s;t;t=(t-1)&s)if(!(bel[t]&bel[s^t])){
int cnt=cross(t,id[i]^bel[t]^(s^t))+cross(s^t,id[i]^t^bel[s^t])+num[id[i]^s]-num[bel[t]^t]-num[bel[s^t]^(s^t)];
(ans[t]+=mod-f[t]*g[s^t]%mod*pw[cnt]%mod*sum[id[i]^bel[s]]%mod)%=mod;
}
}
}
// for(int s=0;s<(1<<n);s++)cout<<f[s]*mul%mod<<" ";cout<<"\n";
// for(int s=0;s<(1<<n);s++)cout<<g[s]*mul%mod<<" ";cout<<"\n";
// for(int s=0;s<(1<<n);s++)cout<<ans[s]*mul%mod<<" ";cout<<"\n";
}
res=0;for(int s=0;s<(1<<n);s++)(res+=ans[s])%=mod;
printf("%lld\n",res*mul%mod*ksm(ksm(4,m))%mod);
}