您的位置:首页 > Web前端 > JavaScript

【bzoj1016】[JSOI2008]最小生成树计数

2018-09-18 23:35 323 查看

1016: [JSOI2008]最小生成树计数

Time Limit: 1 Sec  Memory Limit: 162 MB
Submit: 7429  Solved: 3098

Description

  现在给出了一个简单无向加权图。你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的
最小生成树。(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的)。由于不同的最小生
成树可能很多,所以你只需要输出方案数对31011的模就可以了。

Input

  第一行包含两个数,n和m,其中1<=n<=100; 1<=m<=1000; 表示该无向图的节点数和边数。每个节点用1~n的整
数编号。接下来的m行,每行包含两个整数:a, b, c,表示节点a, b之间的边的权值为c,其中1<=c<=1,000,000,0
00。数据保证不会出现自回边和重边。注意:具有相同权值的边不会超过10条。

Output

  输出不同的最小生成树有多少个。你只需要输出数量对31011的模就可以了。

Sample Input

4 6
1 2 1
1 3 1
1 4 1
2 3 2
2 4 1
3 4 1

Sample Output

8    

题意:

给你一个无向连通图,求生成树权值和最小的生成树的个数。

 

题解:

一开始看错了数据,直接状压了每种权值不同边的取舍情况并在树上倍增维护(x,y)简单路径上最大权值累加方案数。

果不其然拿到了0pts。

实际上并不需要这么复杂。

注意到权值相同的边最多不超过10条,

那么我们考虑MST(最小生成树)的两个性质:

1.每棵MST每种边权的边使用的数量相同。

附带证明(转):

证:设最小生成树有n条边,任意两棵最小生成树分别称为A, B, 如果e是一条边,用w(e)表示该边的权值。

A的边按权值递增排序后为a1, a2,……an满足w(a1)≤w(a2)≤……w(an)

B的边按权值递增排序后为b1, b2,……bn满足w(b1)≤w(b2)≤……w(bn)

设i是两个边列表中,第一次出现不同边的位置,ai≠bi,不妨设w(ai)≥w(bi)。

情形1  如果树A中包含边bi,则一定有j>i使得  bi=aj ,事实上,这时有 w(bi)=w(aj)≥w(ai) ≥w(bi) 故 w(bi)=w(aj)=w(ai),在树A的边列表中交换边ai和 aj的位置并不会影响树A的边权有序列表,两棵树在第i个位置的边变成同一条边。

情形2  树A中并不包含边bi,则把bi加到树A上,形成一个环,由于A是最小生成树,这个环里任意一条边的权值都不大于w(bi) ,另外,这个环里存在边aj不在树B中。因此,有w(aj)≤w(bi),且j>i (因为aj不在B中)。于是,有w(bi)≤w(ai)≤w(aj)≤w(bi),因此

w(ai)= w(aj) = w(bi)。那么在树A中把aj换成bi仍然保持它是一棵最小生成树,并不会影响树A的边权有序列表,并且转换成情形1。

综上,任意两棵最小生成树A,B的边按权值递增排序后的结果相同,即每种边权的边使用数量相同。

 

2.每棵MST内每种边连出的联通块相同。

证:考虑Kruskal算法的过程,对于边权等于v的边集{e},如果eu与ev不在同一个联通块中则连接eu,ev。

即对于每条边,若它沟通的两个点不在边权小于v的边集连出的同一个联通块内,则该边连且必须连。

根据假设,边权小于v的边集连出的联通块是唯一确定的,那么边权等于v连出的联通块也是唯一确定的。

 

得到了这两个性质再回头看这道题显然十分简单。

先求一遍MST,得出权值为v的边使用的数量num(v)。

由于具有相同权值的边数<=10,我们可以将所有边按权值v(从小到大)分块,

状压维护每一块中的边的取舍状态并判断该状态是否满足MST性质,

也就是边数=num(v)并且没有连出环。边数直接count二进制中1的个数,环用并查集维护。

最终答案即为每个块中满足要求的状态数之积。(乘法原理)

注意每次处理完一个块要把它连成联通块,不然无法判断下一个块内的状态是否形成环。

 

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>

using namespace std;
#define MAXN 100005
#define MAXM 500005
#define INF 0x7fffffff
#define ll long long
#define MOD 31011

struct edge{ll u,v,w;}e[MAXM];
struct node{ll l,r,n;}s[MAXM];
ll f[MAXN],tf[MAXN];
inline ll read(){
ll x=0,f=1;
char c=getchar();
for(;!isdigit(c);c=getchar())
if(c=='-')
f=-1;
for(;isdigit(c);c=getchar())
x=x*10+c-'0';
return x*f;
}

bool cmp(edge a,edge b) {return a.w<b.w;}
ll find(ll x) {return f[x]==x?x:f[x]=find(f[x]);}
ll calc(ll x) {ll ans=0;while(x)ans+=(x&1)?1:0,x>>=1;return ans;}
ll find1(ll x) {return tf[x]==x?x:tf[x]=find1(tf[x]);}
bool solve(ll x,ll y,ll N,ll l){
ll cnt=0,cnt1=0;
while(x){
if(x&1){
ll u=e[l+cnt].u,v=e[l+cnt].v;
ll t1=find1(u),t2=find1(v);
if(t1!=t2) tf[t2]=t1,cnt1++;
}
x>>=1;cnt++;
}
return cnt1==y;
}

int main(){
ll N=read(),M=read();
for(ll i=1;i<=M;i++)
e[i].u=read(),e[i].v=read(),e[i].w=read();
sort(e+1,e+1+M,cmp);
for(ll i=1;i<=N;i++) f[i]=i;
ll cnt=0,num=0,maxn=0;
for(ll i=1;i<=M;i++){
ll t1=find(e[i].u),t2=find(e[i].v);
if(e[i].w!=e[i-1].w) {s[cnt].r=i-1;s[++cnt].l=i;}
if(t1!=t2) f[t2]=t1,s[cnt].n+=1,num++;
}
if(num!=N-1) {printf("%d\n",0);return 0;}
for(ll i=1;i<=N;i++) f[i]=i;
s[cnt].r=M; ll sum=1;
for(ll i=1;i<=cnt;i++){
//cout<<s[i].l<<" "<<s[i].r<<" "<<s[i].n<<endl;
if(!s[i].n) continue;
ll j=s[i].l,k=s[i].r,now=0;
for(ll sta=0;sta<(1<<k-j+1);sta++){
if(calc(sta)!=s[i].n) continue;
memcpy(tf,f,sizeof(f));
if(solve(sta,s[i].n,N,s[i].l)) now++;
}
//memcpy(f,tf,sizeof(tf));
//cout<<now<<endl;
sum*=now;sum%=MOD;
for(ll z=j;z<=k;z++){
ll t1=find(e[z].u),t2=find(e[z].v);
if(t1!=t2) f[t2]=t1;
}
}
printf("%lld\n",sum);
return 0;
}

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: