码迷,mamicode.com
首页 > 其他好文 > 详细

COJ262 HDNOIP201206施工方案

时间:2015-06-25 19:02:09      阅读:120      评论:0      收藏:0      [点我收藏+]

标签:

HDNOIP201206施工方案
难度级别:A; 运行时间限制:1000ms; 运行空间限制:51200KB; 代码长度限制:2000000B
试题描述

c国边防军在边境某处的阵地是由n个地堡组成的。工兵连受命来到阵地要进行两期施工。第一期的任务是挖掘暗道让所有地堡互联互通。

现已勘测设计了m条互不相交的暗道挖掘方案,如果这m条暗道都实施挖掘,肯定能达到互联互通的目的。

事实上,适当选择其中n-1个方案挖掘,就能实现互联互通,即从每个地堡出发都能到达其他任何一个地堡(允许经过别的地堡)。

连长精心谋算,在m个设计规划中选取了挖掘总距离最短且能保证互联互通的若干个暗道规划实施了挖掘,

完成了第一期的施工任务后又接受了第二期的施工任务,要求选择一个地堡进行扩建改造,使其能向每个地堡提供弹药。

为了让弹药供应更及时、更快捷,从改扩建的地堡到最远地堡的距离(称为最远输送距离)应当尽量小。

你的任务是先求出第一期施工挖掘的总距离,再求改扩建地堡最远输送距离的最小值。

输入
其中第一行是n和m,m>=n,下面的m行每行3个数xi、yi、zi,表示xi到yi的距离是zi,zi<1000000且m个距离互不相等
输出
两行各一个整数,第一行是第一期的挖掘总距离,第二行是最远输送距离的最小值。
输入示例
4 5
1 2 1
2 3 2
3 4 3
4 1 4
3 1 5
输出示例
6
3
其他说明
第一期挖掘1到2、2到3和3到4的3条暗道,第二期选择3号地堡进行改扩建,最远输送距离是3;60%的数据 n<10且m<20,80%的数据 n<1000且m<2000,100%的数据 n<100000且m<200000
 
 

第一问水水哒,直接MST即可。

第二问有两种做法:

做法一:可以猜想改建的地堡在直径上,因此搞搞即可

这个解法为什么是对的呢?我有一个绝妙的证明,但这里地方太小写不下。

 

技术分享
#include<cstdio>
#include<cctype>
#include<queue>
#include<cstring>
#include<algorithm>
#define rep(s,t) for(int i=s;i<=t;i++)
#define ren for(int i=first[x];i;i=next[i])
using namespace std;
inline int read() {
    int 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;
}
typedef long long LL;
const int maxn=100010;
const int maxm=200010;
struct Edge {
    int u,v,w;
    bool operator < (const Edge& ths) const {return w<ths.w;}
}e[maxm];
int n,m,pa[maxn];
int findset(int x) {return x==pa[x]?x:pa[x]=findset(pa[x]);}
LL ans1;
int first[maxn],to[maxn<<1],next[maxn<<1],dis[maxn<<1],ToT;
void AddEdge(int u,int v,int w) {
    to[++ToT]=v;dis[ToT]=w;next[ToT]=first[u];first[u]=ToT;
    to[++ToT]=u;dis[ToT]=w;next[ToT]=first[v];first[v]=ToT;
}
int d[maxn],vis[maxn],p[maxn];
int bfs(int x) {
    memset(vis,0,sizeof(vis));
    queue<int> Q;Q.push(x);vis[x]=1;d[x]=0;
    while(!Q.empty()) {
        x=Q.front();Q.pop();
        ren if(!vis[to[i]]) vis[to[i]]=1,Q.push(to[i]),d[to[i]]=d[x]+dis[i],p[to[i]]=x;
    }
    int ret=0;
    rep(1,n) if(d[i]>d[ret]) ret=i;
    return ret;
}
int main() {
    n=read();m=read();
    rep(1,m) e[i].u=read(),e[i].v=read(),e[i].w=read();
    rep(1,n) pa[i]=i;
    sort(e+1,e+m+1);
    rep(1,m) {
        int x=findset(e[i].u),y=findset(e[i].v);
        if(x!=y) pa[x]=y,AddEdge(e[i].u,e[i].v,e[i].w),ans1+=e[i].w;
    }
    printf("%lld\n",ans1);
    int s=bfs(1),t=bfs(s),x=t,ans2=1e9;
    while(x!=s) {
        ans2=min(ans2,max(d[t]-d[x],d[x]));
        x=p[x];
    }
    printf("%d\n",ans2);
    return 0;
}
View Code

 

 

做法二:水水哒点分治就好啦。我们可以计算出每个点到其他点的最远距离,记为dp[i]。

当根节点为x时,我们可以更新所有子树的dp值与根节点的dp值,实现要正着反着各扫一遍。

技术分享
#include<cstdio>
#include<cctype>
#include<queue>
#include<cstring>
#include<algorithm>
#define rep(s,t) for(int i=s;i<=t;i++)
#define ren for(int i=first[x];i;i=next[i])
using namespace std;
inline int read() {
    int 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;
}
typedef long long LL;
const int maxn=100010;
const int maxm=200010;
struct Edge {
    int u,v,w;
    bool operator < (const Edge& ths) const {return w<ths.w;}
}e[maxm];
int n,m,pa[maxn];
int findset(int x) {return x==pa[x]?x:pa[x]=findset(pa[x]);}
LL ans1;
int first[maxn],to[maxn<<1],next[maxn<<1],dis[maxn<<1],ToT;
void AddEdge(int u,int v,int w) {
    to[++ToT]=v;dis[ToT]=w;next[ToT]=first[u];first[u]=ToT;
    to[++ToT]=u;dis[ToT]=w;next[ToT]=first[v];first[v]=ToT;
}
int f[maxn],vis[maxn],dp[maxn],s[maxn],root,size;
void getroot(int x,int fa) {
    s[x]=1;int maxs=0;
    ren if(!vis[to[i]]&&to[i]!=fa) {
        getroot(to[i],x);
        s[x]+=s[to[i]];maxs=max(maxs,s[to[i]]);
    }
    f[x]=max(maxs,size-s[x]);
    if(f[x]<f[root]) root=x;
}
int mxdep,dep2,A[maxn];
void dfs(int x,int fa,int dep) {
    dp[x]=max(dp[x],mxdep+dep);dep2=max(dep,dep2);
    ren if(!vis[to[i]]&&to[i]!=fa) dfs(to[i],x,dep+dis[i]);
}
void solve(int x) {
    vis[x]=1;mxdep=0;int TAT=0;
    ren if(!vis[to[i]]) {
        A[TAT++]=i;dep2=0;
        dfs(to[i],0,dis[i]);
        mxdep=max(mxdep,dep2);
    }
    dp[x]=max(dp[x],mxdep);mxdep=0;
    while(TAT--) {
        dep2=0;dfs(to[A[TAT]],0,dis[A[TAT]]);
        mxdep=max(mxdep,dep2);
    }
    ren if(!vis[to[i]]) {
        f[0]=size=s[to[i]];getroot(to[i],root=0);
        solve(root);
    }
}
int main() {
    n=read();m=read();
    rep(1,m) e[i].u=read(),e[i].v=read(),e[i].w=read();
    rep(1,n) pa[i]=i;
    sort(e+1,e+m+1);
    rep(1,m) {
        int x=findset(e[i].u),y=findset(e[i].v);
        if(x!=y) pa[x]=y,AddEdge(e[i].u,e[i].v,e[i].w),ans1+=e[i].w;
    }
    printf("%lld\n",ans1);
    f[0]=size=n;getroot(1,root=0);
    solve(root);
    int ans2=1e9;
    rep(1,n) ans2=min(ans2,dp[i]);
    printf("%d\n",ans2);
    return 0;
}
View Code

 其实我认为第二个做法才是正解

COJ262 HDNOIP201206施工方案

标签:

原文地址:http://www.cnblogs.com/wzj-is-a-juruo/p/4600556.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!