洛谷 P3629 [APIO2010]巡逻(树的直径,贪心)
传送门
解题思路
我们先考虑k==1的情况,这时候一定选取的是树的直径的两个端点。
因为不加这条新路每个边一定走两次,加上了之后道路两个端点之间的路径只需走一遍,所以贪心思想,一定是选取最长的路径的两个端点,即树的直径。
再考虑k==2的情况,一种情况是新加的道路的两个端点之间的路径与加的第一条边两个端点之间的路径没有重边,这样和第一种情况一样,去直径;
另一种情况是新加的道路的两个端点之间的路径与上一次的路径有重复的边,我们发现,这一次不但没有少走一次,反而还要多走一次,所以我们可以把边权改成-1,再做一次树的直径。
因为第一次加道路求树的直径需要记录直径上的点,所以要用两次dfs求;
而第二次加道路因为树上有负边权-1,所以dfs无法求,只能用树形dp求解。
最后注意答案的统计。
AC代码
1 #include<iostream> 2 #include<algorithm> 3 #include<cmath> 4 #include<cstdio> 5 #include<cstring> 6 using namespace std; 7 const int maxn=100005; 8 struct node { 9 int v,next,value; 10 }e[maxn*2]; 11 int n,cnt,p[maxn],pre[maxn],maxd,k,ans,dis[maxn],dp[maxn],maxqaq=-2; 12 void insert(int u,int v,int value){ 13 cnt++; 14 e[cnt].v=v; 15 e[cnt].next=p[u]; 16 e[cnt].value=value; 17 p[u]=cnt; 18 } 19 void dfs(int u,int fa){ 20 for(int i=p[u];i!=-1;i=e[i].next){ 21 int v=e[i].v; 22 if(v==fa){ 23 pre[u]=fa; 24 continue; 25 } 26 dis[v]=dis[u]+e[i].value; 27 dfs(v,u); 28 if(dis[v]>dis[maxd]) maxd=v; 29 } 30 } 31 void dfs2(int u,int fa){ 32 for(int i=p[u];i!=-1;i=e[i].next){ 33 int v=e[i].v; 34 if(v==fa){ 35 continue; 36 } 37 dfs2(v,u); 38 maxqaq=max(maxqaq,dp[u]+dp[v]+e[i].value); 39 dp[u]=max(dp[u],dp[v]+e[i].value); 40 } 41 } 42 int main() 43 { 44 memset(p,-1,sizeof(p)); 45 cin>>n>>k; 46 for(int i=1;i<n;i++){ 47 int u,v; 48 scanf("%d%d",&u,&v); 49 insert(u,v,1); 50 insert(v,u,1); 51 } 52 memset(dis,0,sizeof(dis)); 53 maxd=1; 54 dfs(1,-1); 55 int d1=maxd; 56 memset(dis,0,sizeof(dis)); 57 dfs(d1,-1); 58 int d2=maxd; 59 ans+=dis[d2]; 60 ans=2*n-ans-1; 61 if(k==1) cout<<ans; 62 else{ 63 for(int u=d2;u!=d1;u=pre[u]){ 64 for(int i=p[u];i!=-1;i=e[i].next){ 65 if(e[i].v==pre[u]) e[i].value=-1; 66 } 67 for(int i=p[pre[u]];i!=-1;i=e[i].next){ 68 if(e[i].v==u) e[i].value=-1; 69 } 70 } 71 dfs2(1,-1); 72 ans=ans-maxqaq+1; 73 cout<<ans; 74 } 75 76 return 0; 77 }
//APIO2010 t2