HDU 6005 Pandaland 最小环(最小生成树+LCA)

来源:互联网 发布:linux解压tar.xz文件 编辑:程序博客网 时间:2024/06/05 14:19
Pandaland
Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 745    Accepted Submission(s): 166

Problem Description
Mr. Panda lives in Pandaland. There are many cities in Pandaland. Each city can be treated as a point on a 2D plane. Different cities are located in different locations.
There are also M bidirectional roads connecting those cities. There is no intersection between two distinct roads except their endpoints. Besides, each road has a cost w.
One day, Mr. Panda wants to find a simple cycle with minmal cost in the Pandaland. To clarify, a simple cycle is a path which starts and ends on the same city and visits each road at most once.
The cost of a cycle is the sum of the costs of all the roads it contains.
Input
The first line of the input gives the number of test cases, T. T test cases follow.
Each test case begins with an integer M.
Following M lines discribes roads in Pandaland.
Each line has 5 integers x1,y1,x2,y2, w, representing there is a road with cost w connecting the cities on (x1,y1) and (x2,y2)
Output
For each test case, output one line containing Case #x: y, where x is the test case number (starting from 1) and y is the cost Mr. Panda wants to know.
If there is no cycles in the map, y is 0.

limits
1T50.
1m4000.
10000xi,yi10000.
1w105.

Sample Input
250 0 0 1 20 0 1 0 20 1 1 1 21 0 1 1 21 0 0 1 591 1 3 1 11 1 1 3 23 1 3 3 21 3 3 3 11 1 2 2 22 2 3 3 33 1 2 2 12 2 1 3 24 1 5 1 4
Sample Output
Case #1: 8Case #2: 4
Source
2016 CCPC-Final



        大致题意就是,给你一个图,然后让你在里面找一个最小的环。
        说到找最小环,我很惊叹网上居然没有一个很好的算法。见到的算法都是用floyd的O(N^3)或者dijkstra的O(N^2logN)。但是这些显然都不能满足这题的要求。然后其实本题还加了一个条件,就是一定是一个平面图。一开始还拼命地去想平面图的性质,现在发现其实这些都无所谓。
        这题,只要你联想到之前多校赛的第k小生成树的和,你就会知道如何下手。那题利用树边和非树边进行修改生成树和成环。这题也可以类似。由于要求是最小环,我们可以首先对这个图求最小生成树。根据树的性质,如果我们再添加上任意一条边,就会形成一个环。至于这个环的大小,我们可以用LCA来处理,计算出树上两点距离再加上边权结果就是这个环的大小。考虑最小环,我们只需要枚举这个边,然后找到最小的环即可。时间复杂度为O(V+E+NlogN),NlogN为排序。
        事后看了下网上的题解,惊叹于居然大家都是dijkstra暴力做的,每次枚举一条边,然后把这条边删掉,边的两端设置为起点和终点,跑dijkstra。在跑的时候,每次dij到出现一个权值比当前ans要大的时候就停止dij。这样也是可以水过的,虽然理论是肯定会T的。具体见代码吧,感觉方法还是非常巧妙的,代码是队友写的,跟我风格不太符合:
#include<bits/stdc++.h>#define N 8010#define INF 100000000000#define LL long longusing namespace std;typedef pair<int,int> P;LL i,j,k,l,n,t,h;map<P,LL>s;vector <int> a[N],b[N];bool f[N];LL L[N],fa[N],anc[N][20],d[N],m;struct edge{    int a,b,c;    bool operator < (const edge x)const    {        return c<x.c;    }}E[N];void lable(LL x,LL ffa,LL p)//LCA标号{    for (LL i=0;i<a[x].size();i++)if (a[x][i]!=ffa)    {        d[a[x][i]]=d[x]+b[x][i];        fa[a[x][i]]=x;        L[a[x][i]]=p+1;        lable(a[x][i],x,p+1);    }}void preprocess()//倍增预处理{    for (LL i=1;i<=n;i++)    {        anc[i][0]=fa[i];        for (LL j=1;(1<<j)<=n;j++) anc[i][j]=-1;    }    for (LL j=1;(1<<j)<=n;j++)        for (LL i=1;i<=n;i++)        if (anc[i][j-1]!=-1)        {            LL x=anc[i][j-1];            anc[i][j]=anc[x][j-1];        }}LL query(LL p,LL q)//求两点LCA{    LL log,i;    if (L[p]<L[q]) swap(p,q);    for (log=1;(1<<log)<=L[p];log++);log--;    for (LL i=log;i>=0;i--)        if (L[p]-(1<<i)>=L[q])        {            p=anc[p][i];        }    if (p==q)return p; //LCA?ap    for (LL i=log;i>=0;i--)        if (anc[p][i]!=-1 && anc[p][i]!=anc[q][i])    {        p=anc[p][i];        q=anc[q][i];    }    return fa[p];//LCA?afa[p]}int faa[N];int getfa(int x)//并查集……{    if (faa[x]==0)return x;else    {        int t=getfa(faa[x]);        faa[x]=t;        return t;    }}bool used[N];int main(){    LL T,tt=0;    scanf("%I64d",&T);    while (T--)    {        t=0;s.clear();        memset(f,0,sizeof f);memset(d,0,sizeof d);memset(used,0,sizeof used);        memset(a,0,sizeof a);memset(b,0,sizeof b);        memset(faa,0,sizeof faa);memset(fa,0,sizeof fa);        memset(L,0,sizeof L);memset(anc,0,sizeof anc);        m=100000000000;        scanf("%I64d",&n);        for (i=1;i<=n;i++)        {            LL x,y;            scanf("%I64d%I64d",&x,&y);            if (!s[P(x,y)]) {j=++t;s[P(x,y)]=t;}else j=s[P(x,y)];            scanf("%I64d%I64d",&x,&y);            if (!s[P(x,y)]) {k=++t;s[P(x,y)]=t;}else k=s[P(x,y)];            scanf("%I64d",&l);            E[i]=edge{j,k,l};        }        sort(E+1,E+n+1);        for (i=1;i<=n;i++)//确定最小生成树,标记树边        {            int p=getfa(E[i].a),q=getfa(E[i].b);            if (p!=q)            {                used[i]=1;                int v=E[i].a,u=E[i].b;                a[v].push_back(u);b[v].push_back(E[i].c);//建树                a[u].push_back(v);b[u].push_back(E[i].c);                faa[p]=q;            }        }        int nn=n;        n=t;        for (i=1;i<=n;i++)        {            if (fa[i]==0)            {                fa[i]=i;                lable(i,-1,0);            }        }        preprocess();        for (i=1;i<=nn;i++)if (!used[i])//枚举非树边求最小环        {            int v=E[i].a,u=E[i].b;            LL w=d[v]+d[u]-d[query(v,u)]*2;            if (w+E[i].c<m)m=w+E[i].c;        }        if (m==100000000000) m=0;        printf("Case #%I64d: %I64d\n",++tt,m);    }    return 0;}

阅读全文
0 0
原创粉丝点击