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; }
pain and happy in the cruel world.