bzoj1912: [Apio2010]patrol 巡逻

曾系尴尬今天发现自己没写这题

不建新边的话其实每条边都是走两次

假如建一条新边相当于一条树上路径只用走一次

那么当然选直径了

但是建两条的话有可能树上路径有重叠部分,而每条边都需要经过,那么就会多减两次

所以第一次选完以后把直径上的边变负容斥下

恶心的是树上边权有负的时候是不能用两次dfs的方法找直径的。。。。。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;

struct node
{
    int x,y,d,next;
}a[210000];int len,last[110000];
void ins(int x,int y,int d)
{
    len++;
    a[len].x=x;a[len].y=y;a[len].d=d;
    a[len].next=last[x];last[x]=len;
}
int d[2][110000],p[2][110000];
void dfs(int x,int fr)
{
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(y!=fr)
        {
            dfs(y,x);
            if(d[0][y]+a[k].d>d[0][x])
            {
                d[1][x]=d[0][x];
                p[1][x]=p[0][x];
                d[0][x]=d[0][y]+a[k].d;
                p[0][x]=k;
            }
            else if(d[0][y]+a[k].d>d[1][x])
            {
                d[1][x]=d[0][y]+a[k].d;
                p[1][x]=k;
            }
        }
    }
}

int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    int n,K,x,y;
    scanf("%d%d",&n,&K);
    len=1;memset(last,0,sizeof(last));
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        ins(x,y,1),ins(y,x,1);
    }
    int ans=(n-1)*2;
    for(int t=1;t<=K;t++)
    {
        memset(d,0,sizeof(d));
        memset(p,0,sizeof(p));
        dfs(1,0);
        int id=1;
        for(int i=2;i<=n;i++)
            if(d[0][i]+d[1][i]>d[0][id]+d[1][id])id=i;
        
        ans=ans-(d[0][id]+d[1][id])+1;
        for(int i=0;i<=1;i++)
            for(int k=p[i][id];k;k=p[0][a[k].y])
                a[k].d=-a[k].d, a[k^1].d=-a[k^1].d;
    }
    printf("%d\n",ans);
    return 0;
}

 

posted @ 2018-10-09 16:27  AKCqhzdy  阅读(120)  评论(0编辑  收藏  举报