zoukankan      html  css  js  c++  java
  • P4180 【模板】严格次小生成树[BJWC2010]

    题目描述

    小C最近学了很多最小生成树的算法,Prim算法、Kurskal算法、消圈算法等等。正当小C洋洋得意之时,小P又来泼小C冷水了。小P说,让小C求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说:如果最小生成树选择的边集是EM,严格次小生成树选择的边集是ES,那么需要满足:(value(e)表示边e的权值) ∑e∈EMvalue(e)<∑e∈ESvalue(e)sum_{e in E_M}value(e)<sum_{e in E_S}value(e)eEMvalue(e)<eESvalue(e)

    这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。

    输入输出格式

    输入格式:

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

    输出格式:

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

    输入输出样例

    输入样例#1: 复制
    5 6
    1 2 1 
    1 3 2 
    2 4 3 
    3 5 4 
    3 4 3 
    4 5 6 
    输出样例#1: 复制
    11

    说明

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


    用非严格最小生成树去边的方法显然时间和正确性都是不对的

    思路:跑完最小生成树后穷举在树上添加每一条非树边(x,y),这条边会与树上x到y的路径形成一个环,这时去掉环上严格次大边即可形成一棵新的生成树

    方法:

    • 跑出一棵最小生成树并标记树上的边,(所以我当时到底是怎么想到用prim而不用kruskal的TAT
    • 用倍增预处理出f[i][j]每个点的2j级祖先和g[i][j][01] 节点i到他的2j级祖先路径上的最大值和次大值
    • 穷举添加每一条非树边(x,y),用倍增求lca(x,y)同时找出树上路径中的最大值和次大值
    • 如果最大值与非树边xy相等则比较原答案和最小生成树的边权和减去次大边加上edge[x][y],f否则比较原答案和最小生成树的边权和减去最大边加上edge[x][y]

    #include<iostream>
    #include<stdio.h>
    #include<queue>
    #include<cstring>
    using namespace std;
    
    int i,m,h,n,j,k,a[700000],f[700000][25],g[700000][25][2],ver[700000]={1},nex[700000],edge[700000],head[700000],cnt;
    priority_queue<pair<int,int> > q;
    int w[700000],x,y,deep[700000];
    bool b[700000],bl[700000];
    long long ans=2147483647000000,sum;
    void add(int x,int y,int z,int r)
    {
        cnt+=1;
        ver[cnt]=y;
        nex[cnt]=head[x]; 
        head[x]=cnt;
        edge[cnt]=z;
        w[cnt]=r;
    }
    
    void prim()
    {
        memset(a,0x3f,sizeof(a)); a[1]=0;
        q.push(make_pair(0,0));
        for(int l=1;l<=n;l++)
        {
            while(b[ver[q.top().second]]) q.pop();
            int t=q.top().second; q.pop();
            b[ver[t]]=1;	bl[t]=bl[w[t]]=1;
            for(int i=head[ver[t]];i;i=nex[i])
            {
                int e=ver[i];
                if(edge[i]<a[e])
                {
                    a[e]=edge[i];
                    q.push(make_pair(-a[e],i));
                }
            }
        }
        b[0]=bl[0]=0; ver[0]=0;
    }
    
    void dfs(int x,int fa)
    {
        for(int i=head[x];i;i=nex[i])
        {
            int t=ver[i];
            if(bl[i]&&(t!=fa)) 
            {
                deep[t]=deep[x]+1;
                g[t][0][0]=edge[i];
                sum+=(long long)edge[i];
                f[t][0]=x;
                dfs(t,x);
            }
        }
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for(i=1;i<=m;i++)
        {
            scanf("%d%d%d",&x,&h,&k);
            add(x,h,k,cnt+2);
            add(h,x,k,cnt);
        }
        for(i=0;i<=n;i++) g[i][0][1]=-0x3f3f3f3f;
        deep[1]=1; prim(); dfs(1,0);
        for(j=1;j<20;j++)
            for(i=1;i<=n;i++) 
                f[i][j]=f[f[i][j-1]][j-1];
        for(j=1;j<20;j++) 
            for(i=1;i<=n;i++)	
            {
                if(!f[i][j]) continue;
                g[i][j][0]=max(g[i][j-1][0],g[f[i][j-1]][j-1][0]);
                if(g[i][j-1][0]==g[f[i][j-1]][j-1][0]) g[i][j][1]=max(g[i][j-1][1],g[f[i][j-1]][j-1][1]);
                if(g[i][j-1][0]<g[f[i][j-1]][j-1][0])  g[i][j][1]=max(g[i][j-1][0],g[f[i][j-1]][j-1][1]);
                if(g[i][j-1][0]>g[f[i][j-1]][j-1][0])  g[i][j][1]=max(g[i][j-1][1],g[f[i][j-1]][j-1][0]);
            }
        for(i=1;i<=cnt;i++)
        {
            if(bl[i]) continue;
            int maxx=-0x3f3f3f3f,mmax=-0x3f3f3f3f;
            x=ver[i]; y=ver[w[i]];
            if(deep[x]<deep[y]) swap(x,y);
            for(j=20;j>=0;j--) 
            if(deep[f[x][j]]>=deep[y]) 
            {
                if(g[x][j][0]>maxx) 
                {
                    mmax=maxx,maxx=g[x][j][0];
                    if(g[x][j][1]>mmax) mmax=g[x][j][1];
                }
                else if((g[x][j][0]>mmax)&&(maxx!=g[x][j][0])) mmax=g[x][j][0];
                
                x=f[x][j];
            }
            for(j=20;j>=0;j--)
            {
                if(f[x][j]==f[y][j]) continue;
                if(g[x][j][0]>maxx) 
                {
                    mmax=maxx,maxx=g[x][j][0];
                    if(g[x][j][1]>mmax) mmax=g[x][j][1];
                }
                else if(g[x][j][0]>mmax&&(maxx!=g[x][j][0])) mmax=g[x][j][0];
                
                if(g[y][j][0]>maxx) 
                {
                    mmax=maxx,maxx=g[y][j][0];
                    if(g[y][j][1]>mmax) mmax=g[y][j][1];
                }
                else if((g[y][j][0]>mmax)&&(maxx!=g[y][j][0])) mmax=g[y][j][0];
                y=f[y][j]; x=f[x][j];
            }
            if(x!=y)
            {
                if(g[x][0][0]>maxx) 
                {
                    mmax=maxx,maxx=g[x][0][0];
                    if(g[x][0][1]>mmax) mmax=g[x][0][1];
                }
                else if((g[x][0][0]>mmax)&&(maxx!=g[x][0][0])) mmax=g[x][0][0];
                
                if(g[y][0][0]>maxx) 
                {
                    mmax=maxx,maxx=g[y][0][0];
                    if(g[y][0][1]>mmax) mmax=g[y][0][1];
                }
                else if((g[y][0][0]>mmax)&&(maxx!=g[x][0][0])) mmax=g[y][0][0];
            }
            if(edge[i]==maxx) ans=min(ans,(long long)sum-mmax+maxx);
            else ans=min(ans,(long long)sum-maxx+edge[i]);
            bl[w[i]]=1;
        }
        printf("%lld",ans);
    }
    
  • 相关阅读:
    API
    API
    for in
    event flow
    object
    Report of program history
    正则表达式
    伪类与伪元素
    Position
    js学习之原型(补充)
  • 原文地址:https://www.cnblogs.com/ZUTTER/p/9347268.html
Copyright © 2011-2022 走看看