bzoj 1912: [Apio2010]patrol 巡逻

Description

solution

正解:贪心+DP
首先对于K=1的情况我们可以发现答案是 \((n-1)*2\)-树的直径+1

K=2同理,我们也要再找出一条不相交的树的直径,然后怎么保证不相交呢?
其实只需要把第一次求得的直径上的点赋值为-1即可
如果一条边再两次都出现,就相互抵消了,去掉抵消的部分,就完美的形成了不相交的两条链了,非常巧妙
另外,两遍dfs求直径的方法在有负权的情况下是用不了了,需要DP求解,具体是记录最长链和次长链 相加

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
const int N=100005;
int n,K,head[N],to[N<<1],nxt[N<<1],dis[N<<1],num=1;
il void link(int x,int y,int z){
   nxt[++num]=head[x];to[num]=y;dis[num]=z;head[x]=num;}
int f[N][2],ans=0,ansid=0,maxid[N],cmax[N];
il void dfs(RG int x,int last){
   RG int u,tmp;
   for(int i=head[x];i;i=nxt[i]){
      u=to[i];if(u==last)continue;
      dfs(u,x);tmp=f[u][0]+dis[i];
      if(tmp>f[x][0])
         cmax[x]=maxid[x],maxid[x]=i,f[x][1]=f[x][0],f[x][0]=tmp;
      else if(tmp>f[x][1])f[x][1]=tmp,cmax[x]=i;
   }
   if(f[x][0]+f[x][1]>=ans)ans=f[x][0]+f[x][1],ansid=x;
}
il void Rev(int i){dis[i]=dis[i^1]=-1;}
void solve(){
   Rev(maxid[ansid]);Rev(cmax[ansid]);
   RG int now=to[maxid[ansid]];
   while(maxid[now]){
      Rev(maxid[now]);
      now=to[maxid[now]];
   }
   now=to[cmax[ansid]];
   while(maxid[now]){
      Rev(maxid[now]);
      now=to[maxid[now]];
   }
}
void Clear(){
   memset(f,0,sizeof(f));memset(maxid,0,sizeof(maxid));
   memset(cmax,0,sizeof(cmax));ans=0;ansid=0;
}
void work()
{
   int x,y,ret=0;
   scanf("%d%d",&n,&K);
   for(RG int i=1;i<n;i++){
      scanf("%d%d",&x,&y);
      link(x,y,1);link(y,x,1);
   }
   ret+=(n-1)<<1;dfs(1,1);ret-=ans-1;
   if(K==1){printf("%d\n",ret);return ;}
   solve();Clear();dfs(1,1);ret-=ans-1;
   printf("%d\n",ret);
}
 
int main()
{
    work();
    return 0;
}
posted @ 2017-10-23 00:03  Hxymmm  阅读(112)  评论(0编辑  收藏  举报