• 【BZOJ1977】[BeiJing2010组队]次小生成树 Tree 最小生成树+倍增


    【BZOJ1977】[BeiJing2010组队]次小生成树 Tree

    Description

    小 C 最近学了很多最小生成树的算法,Prim 算法、Kurskal 算法、消圈算法等等。 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了。小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说: 如果最小生成树选择的边集是 EM,严格次小生成树选择的边集是 ES,那么需要满足:(value(e) 表示边 e的权值)  这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。

    Input

    第一行包含两个整数N 和M,表示无向图的点数与边数。 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z。

    Output

    包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)

    Sample Input

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

    Sample Output

    11

    HINT

    数据中无向图无自环; 50% 的数据N≤2 000 M≤3 000; 80% 的数据N≤50 000 M≤100 000; 100% 的数据N≤100 000 M≤300 000 ,边权值非负且不超过 10^9 。

    题解:结论!次小生成树一定可以由 最小生成树+一条非树边-这条非树边覆盖的一条边 得到。证明方法。。。假设我们要加入两条边,先加一条边a使总权值变大,再加一条边b使权值变小。那么b所替换掉的边一定在a形成的环上,因为a是非树边,所以a的权值比环上的所有边都大,所以b替换掉的边权值一定不会大于a,所以我们就白加入a这条边了。(奇怪的证明)

    所以我们要替换那条边呢?如果它所覆盖的边的最大值=这条非树边的权值,则替换它所覆盖的边的次大值;如果不相等,则直接替换最大值。这就需要我们求出一条路径上的权值最大值和次大值,可以用树剖,当然我觉得倍增更优美一点~

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    const int maxn=100010;
    int n,m,cnt;
    ll ans,sum;
    struct edge
    {
    	int a,b,val,used;
    }p[maxn*3];
    struct node
    {
    	int x,y;
    	node() {x=0,y=-1;}
    	node(int _){x=_,y=0;}
    	node operator + (const node &a)const
    	{
    		node b;
    		if(x<a.x)	b.x=a.x,b.y=max(x,a.y);
    		if(x>a.x)	b.x=x,b.y=max(y,a.x);
    		if(x==a.x)	b.x=x,b.y=max(y,a.y);
    		return b;
    	}
    }s[19][maxn];
    int to[maxn<<1],next[maxn<<1],head[maxn],val[maxn<<1],fa[19][maxn],dep[maxn],Log[maxn],f[maxn];
    bool cmp(const edge &a,const edge &b)
    {
    	return a.val<b.val;
    }
    int find(int x)
    {
    	return (f[x]==x)?x:f[x]=find(f[x]);
    }
    inline void add(int a,int b,int c)
    {
    	to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++;
    }
    inline node ask(int a,int b)
    {
    	if(dep[a]<dep[b])	swap(a,b);
    	node ret;
    	for(int i=Log[dep[a]-dep[b]];i>=0;i--)	if(dep[fa[i][a]]>=dep[b])	ret=ret+s[i][a],a=fa[i][a];
    	if(a==b)	return ret;
    	for(int i=Log[dep[a]];i>=0;i--)	if(fa[i][a]!=fa[i][b])	ret=ret+s[i][a]+s[i][b],a=fa[i][a],b=fa[i][b];
    	return ret+s[0][a]+s[0][b];
    }
    void dfs(int x)
    {
    	for(int i=head[x];i!=-1;i=next[i])	if(to[i]!=fa[0][x])
    		fa[0][to[i]]=x,dep[to[i]]=dep[x]+1,s[0][to[i]]=node(val[i]),dfs(to[i]);
    }
    inline int rd()
    {
    	int ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
    	return ret*f;
    }
    int main()
    {
    	n=rd(),m=rd();
    	int i,j,a,b;
    	for(i=1;i<=m;i++)	p[i].a=rd(),p[i].b=rd(),p[i].val=rd();
    	sort(p+1,p+m+1,cmp);
    	for(i=1;i<=n;i++)	f[i]=i;
    	memset(head,-1,sizeof(head));
    	for(i=1;i<=m;i++)
    	{
    		a=find(p[i].a),b=find(p[i].b);
    		if(a!=b)
    		{
    			add(p[i].a,p[i].b,p[i].val),add(p[i].b,p[i].a,p[i].val),f[a]=b,p[i].used=1,sum+=p[i].val;
    			if(cnt==(n-1)<<1)	break;
    		}
    	}
    	dep[1]=1,dfs(1);
    	for(i=2;i<=n;i++)	Log[i]=Log[i>>1]+1;
    	for(j=1;(1<<j)<=n;j++)	for(i=1;i<=n;i++)	fa[j][i]=fa[j-1][fa[j-1][i]],s[j][i]=s[j-1][i]+s[j-1][fa[j-1][i]];
    	ans=1ll<<60;
    	for(i=1;i<=m;i++)	if(!p[i].used)
    	{
    		node tmp=ask(p[i].a,p[i].b);
    		if(tmp.x!=p[i].val&&tmp.x>0)	ans=min(ans,sum+p[i].val-tmp.x);
    		if(tmp.y>0)	ans=min(ans,sum+p[i].val-tmp.y);
    	}
    	printf("%lld",ans);
    	return 0;
    }
  • 相关阅读:
    九度OJ 1031:xxx定律 (基础题)
    九度OJ 1030:毕业bg (01背包、DP)
    九度OJ 1029:魔咒词典 (排序)
    九度OJ 1028:继续畅通工程 (最小生成树)
    九度OJ 1027:欧拉回路 (欧拉回路)
    九度OJ 1026:又一版 A+B (进制转换)
    九度OJ 1025:最大报销额 (01背包、DP)
    九度OJ 1024:畅通工程 (最小生成树)
    九度OJ 1023:EXCEL排序 (排序)
    九度OJ 1022:游船出租 (统计)
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7603752.html
Copyright © 2020-2023  润新知