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;
}