[BZOJ1912][Apio2010]patrol 巡逻(dfs+并查集+树形dp)

来源:互联网 发布:数据恢复软件哪个好 编辑:程序博客网 时间:2024/05/18 22:45

题目描述

传送门

题解

k=1时,首先使所有的路权都为1,求树上最长链,答案为2×(n-1)-(len1-1)
k=2时,在k=1的基础上,将第一次选取的链上的边权都赋为-1,然后再求树的直径记为len2,答案为2×(n-1)-(len1-1)-(len2-1)
注意第二次求树的直径有负边权dfs就不管用了,用树形dp

代码

#include<iostream>#include<cstring>#include<cstdio>using namespace std;const int max_n=1e5+5;const int max_m=max_n;const int max_e=max_m*2;const int INF=2e9;int n,k,maxn,root,oldroot,len1,len2;int tot,point[max_n],next[max_e],v[max_e],c[max_e];int father[max_n],h[max_n],f[max_n],F[max_n],G[max_n];struct hp{int x,y;}edge[max_m];inline void add(int x,int y){    ++tot; next[tot]=point[x]; point[x]=tot; v[tot]=y;    ++tot; next[tot]=point[y]; point[y]=tot; v[tot]=x;}inline void addedge(int x,int y,int z){    ++tot; next[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;    ++tot; next[tot]=point[y]; point[y]=tot; v[tot]=x; c[tot]=z;}inline void dfs(int x,int fa,int dep){    father[x]=fa; h[x]=dep; if (h[x]>maxn){maxn=h[x];root=x;}    for (int i=point[x];i;i=next[i])      if (v[i]!=fa)        dfs(v[i],x,dep+1);}inline int find(int x){if (x==f[x]) return x; else return f[x]=find(f[x]);}inline void merge(int x,int y){int f1=find(x),f2=find(y); f[f1]=f2;}inline void treedp(int x,int fa){    for (int i=point[x];i;i=next[i])      if (v[i]!=fa){        treedp(v[i],x);        if (F[v[i]]+c[i]>F[x]){            G[x]=F[x];            F[x]=F[v[i]]+c[i];        }        else G[x]=max(G[x],F[v[i]]+c[i]);      }    len2=max(len2,F[x]+G[x]);}int main(){    scanf("%d%d",&n,&k);    for (int i=1;i<n;++i){        scanf("%d%d",&edge[i].x,&edge[i].y);        add(edge[i].x,edge[i].y);    }    maxn=0; dfs(1,0,0); oldroot=root;    memset(h,0,sizeof(0)),maxn=0; dfs(root,0,0);    if (k==1){        printf("%d\n",maxn+1+(n-1-maxn)*2);        return 0;    }    len1=maxn;    tot=0; memset(point,0,sizeof(point)); memset(next,0,sizeof(next)); memset(v,0,sizeof(v)); memset(c,0,sizeof(c));    for (int i=1;i<=n;++i) f[i]=i;    while (root!=oldroot){        addedge(father[root],root,-1);        merge(father[root],root);        root=father[root];    }    for (int i=1;i<n;++i)      if (find(edge[i].x)!=find(edge[i].y)){        addedge(edge[i].x,edge[i].y,1);        merge(edge[i].x,edge[i].y);      }    len2=-INF;    treedp(1,0);    printf("%d\n",2*(n-1)-len1+1-len2+1);}
0 0
原创粉丝点击