BZOJ-1977 次小生成樹 Tree 樹上倍增LCA+Kruskal+位運算

char哥剛了這題一天,被我2小時拍完....
-----DaD3zZ:char哥,我們競速吧,我覺得我能跑的比你快;
-----Char哥:寫了多少行...我只有65行
-----DaD3zZ:我....148行,但我沒壓行!!
     一會兒後....
-----DaD3zZ:char哥我A的你比快很多
-----Char哥:我剛剛又看了看,感覺能拿到40~50行左右!!!

鬼畜的壓行超人char哥Etienne!!

1977: [BeiJing2010組隊]次小生成樹 Tree
Time Limit: 10 Sec Memory Limit: 512 MB
Submit: 2629 Solved: 643
[Submit][Status][Discuss]

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 。

Source

做法:
此題爲嚴格次小生成樹,所以做法有些麻煩
首先求出最小生成樹
並從剩餘邊中依次枚舉,並進行操作
將這條邊加入樹,形成一個環,那麼刪掉此環上的一條邊,就會從新出現一棵生成樹,那麼如何高效的去找要刪去的邊
倍增LCA
倍增維護3個值,father【】維護LCA,f維護路徑上的最大值,g維護路徑上的嚴格次大值
維護次大的值的意義在於:
對於刪環上的邊,如果添加進來的邊與環上最大值不同,那麼直接刪換上最大值,如果與最大值相同,就必須刪次大值(因爲刪除最大值之後仍爲最小生成樹)(第一次忘了進行討論了…)
那麼只需要枚舉每一條剩餘的邊來判斷就好

code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0' && ch) {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
#define maxn 100010
#define maxm 300010
int n,m,num,minn;
long long ans=0;
struct data
{
    int from,to,val;bool used;
    bool operator < (const data & A) const
        {
            return val<A.val;
        }
}edge[maxm];
int fa[maxn];
struct dat{int to,next,from,val;}road[maxn*2];
int head[maxn],tot;
//init
void add(int u,int v,int w)
{
    tot++;
    road[tot].to=v; road[tot].from=u; road[tot].val=w;
    road[tot].next=head[u]; head[u]=tot;
}
void insert(int u,int v,int w)
{
    add(u,v,w); add(v,u,w);
}
//add road
void init()
{
    for (int i=1; i<=n; i++) fa[i]=i;
}
int find(int x)
{
    if (fa[x]==x) return x;
    fa[x]=find(fa[x]); return fa[x];
}
bool merge(int x,int y)
{
    int f1=find(x),f2=find(y);
    if (f1!=f2)
        {fa[f1]=f2;return 1;}
    return 0;
}
//kruskal
int father[maxn][25],f[maxn][25],g[maxn][25];
int deep[maxn];
void dfs(int x,int last)
{
    //printf("%d %d\n",x,last);
    for (int i=1; i<=20; i++)
        {
            if(deep[x]<(1<<i))break;
            father[x][i]=father[father[x][i-1]][i-1];
            f[x][i]=max(f[x][i-1],f[father[x][i-1]][i-1]);
            if (f[x][i-1]==f[father[x][i-1]][i-1])
                g[x][i]=max(g[x][i-1],g[father[x][i-1]][i-1]);
            else
                g[x][i]=min(f[x][i-1],f[father[x][i-1]][i-1]),
                g[x][i]=max(g[x][i],g[x][i-1]),
                g[x][i]=max(g[x][i],g[father[x][i-1]][i-1]);
        }
    for (int i=head[x]; i; i=road[i].next)
        if (last!=road[i].to)
            {
                father[road[i].to][0]=x;
                deep[road[i].to]=deep[x]+1;
                f[road[i].to][0]=road[i].val;
                dfs(road[i].to,x);
            }
}
void swap(int &x,int &y)
{
    int tmp=x; x=y; y=tmp;
}
int LCA(int x,int y)
{
    if (deep[x]<deep[y]) swap(x,y);
    int d=deep[x]-deep[y];
    for (int i=0; i<=20; i++)
        if ((1<<i)&d) x=father[x][i];
    for (int i=20; i>=0; i--)
        if (father[x][i]==father[y][i]) continue;
            else x=father[x][i],y=father[y][i];
    if (x==y) return x;
    return father[x][0];
}
int work(int x,int lca,int val)
{
    int maxx1=0,maxx2=0;
    int d=deep[x]-deep[lca];
    for (int i=0; i<=20; i++)
        {
            if (d&(1<<i))
                {
                    if (f[x][i]>maxx1)
                        maxx2=maxx1,maxx1=f[x][i];
                    maxx2=max(maxx2,g[x][i]);
                    x=father[x][i];
                }
        }
    if (maxx1!=val) minn=min(minn,val-maxx1);
               else minn=min(minn,val-maxx2);
}
void solve(int x,int val)
{
    int u=edge[x].from,v=edge[x].to,lca=LCA(u,v);
    work(u,lca,val); work(v,lca,val);
}
int main()
{
//  freopen("mst2.in","r",stdin);
//  freopen("mst2.out","w",stdout);
    n=read(),m=read();
    for (int i=1; i<=m; i++) 
        edge[i].from=read(),edge[i].to=read(),edge[i].val=read();
    sort(edge+1,edge+m+1);
    int cnt=0; init();
    //puts("OK");
    while (num<n-1) 
        {
            cnt++;
            if (merge(edge[cnt].from,edge[cnt].to)==1) 
                num++,ans+=edge[cnt].val,edge[cnt].used=1,
                insert(edge[cnt].from,edge[cnt].to,edge[cnt].val);
                //printf("%d %d %d %d\n",edge[cnt].from,edge[cnt].to,edge[cnt].val,edge[cnt].used);
        }
    //printf("%d\n",ans);
    dfs(1,0);
    //puts("OK");
    minn=0x7fffffff;
    for (int i=1; i<=m; i++)
        {/*printf("%d is OK\n",i);*/if (!edge[i].used) solve(i,edge[i].val);}
    printf("%lld\n",ans+minn);
    return 0;
}

對比圖:
char哥:這裏寫圖片描述
自己:這裏寫圖片描述
YveH大爺:這裏寫圖片描述
居然跑的比char哥慢,不開心!!!!
不過跑的比YveH大爺快很多QAQ…
不過還好自己 邊+調 用了1h+
而char哥用來1afternoon&1night+

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章