[APIO2010]巡逻 题解

题目:传送门

题目大意:一棵树,添加k条边使得遍历所有的点的总代价最小,其中1<=k<=2;

首先呢:不建道路,路线总长度2*(n-1);

我们分析当k等于1,此时的最优解无疑是将边加到直径两端;那么答案就是2*(n-1)-d+1;这样你会得到30分;

其实我们考虑k=2时,对于一棵树,我们加边会形成一个环,加两条边形成两个环,我们就要考虑这两个环的关系了;

1.两个环上的边不重叠;

2.两个环上的边有重叠部分;

对于第一种情况,我们只需要再次找一下最长链,忽略直径哈,答案减小;

那么对于第二种,如果我们将其作为加1条边的请况,那么两个环重叠的不会将不会被经过,和题目要求不符,重叠部分则需要经过两次,所以我们需要让巡逻车在适当时候重新巡逻这些边,并且返回,最终的结果是我们让两个环上的重叠的边经过了两次;

所以:

1、求树的直径L1,将L1上的边权取反,变为-1;
2、再求树的直径L2,答案即为2(n−1)−L1+1−L2+1=2∗n−L1−L2;

如果L2这条直径包含L1取反的部分,就相当于重叠,减掉(L1-1),重叠的边经过了一次,减掉(L2-1),把重叠部分加回来,变为需要经过两次;

时间复杂度O(N);

对于找树的直径,有bfs(dfs)和dp两种做法:

但是bfs的做法遇到负权好像不太行,但是dp可以,但是dp好像只可以求个直径;

#include<bits/stdc++.h>
using namespace std;
#define N 500010
template<typename T>inline void read(T &x)
{
    x=0;T f=1,ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch))  {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x*=f;
}
struct gg {
    int y,next,v;
}a[N<<1];
int lin[N],vis[N],dis[N],dis1[N],father[N],tot=1,n,k,ans,d,x1,x2;
inline void add(int x,int y,int z) {
    a[++tot].next=lin[x];
    a[tot].y=y;
    a[tot].v=z;
    lin[x]=tot;
}
queue<int> q;
inline int bfs(int s) {
    memset(vis,0,sizeof(vis));
    int point=s;
    q.push(s);
    dis[s]=0;vis[s]=1;
    father[s]=0;
    while(!q.empty()) {
        int x=q.front();q.pop();
        for(int i=lin[x];i;i=a[i].next) {
            int y=a[i].y;
            if(!vis[y]) {
                vis[y]=1;q.push(y);
                dis[y]=dis[x]+a[i].v;
                father[y]=x;
                if(dis[point]<dis[y]) point=y;
            }
        }
    }
    return point;
}
inline void dp(int u) {
    int maxn=0;
    for (int i=lin[u];i;i=a[i].next) {
        int v=a[i].y;
        if (v!=father[u]) {
            dp(v);
            ans=max(ans,maxn+dis1[v]+a[i].v);
            maxn=max(maxn,dis1[v]+a[i].v);
        }
    }
    ans=max(ans,maxn);
    dis1[u]=maxn;
}
inline void get() {
    x1=bfs(1);
    x2=bfs(x1);
    bfs(1);
    memset(vis,0,sizeof(vis));
    if(dis[x1]<dis[x2]) swap(x1,x2);
    vis[x1]=vis[x2]=1;
    while(dis[x1]>dis[x2]) {
        x1=father[x1];
        vis[x1]=1;
        ++d;
    }
    while (x1!=x2) {
        x1=father[x1];
        x2=father[x2];
        vis[x1]=vis[x2]=1;
        d+=2;
    }
}
inline void tab(int x) {
    for(int i=lin[x];i;i=a[i].next) {
        int y=a[i].y;
        if(y!=father[x]) {
            if(vis[x]&&vis[y]) {
                a[i].v=a[i^1].v=-1;
            }
            tab(y);
        }
    }
}
int main() {
    //freopen("data.in","r",stdin);
    //freopen("data.ans","w",stdout);
    read(n);read(k);
    int x,y;
    for(int i=1;i<n;i++) {
        read(x);read(y);
        add(x,y,1);add(y,x,1);
    }
    get();
    if(k==1) {
        printf("%d\n",2*(n-1)-d+1);
        exit(0); 
    }
    if(d==n-1) {
        printf("%d\n",n+1);
        exit(0);
    }
    else {
        tab(1);
        dp(1);
        printf("%d\n",2*n-ans-d);
    }    
    return 0;
}
View Code

 

 

posted @ 2019-05-04 13:18  Tyouchie  阅读(184)  评论(0编辑  收藏  举报