zoukankan      html  css  js  c++  java
  • 洛谷 P3629 【[APIO2010]巡逻】

    树的直径(DFS+DP)

    首先考虑$k=0$,也就是单只有一棵树的时候,由DFS深度优先遍历可知,搜一遍正好每条边都进出一次,答案是$2(n-1)$

    然后考虑$k=1$的情况,也就是出现有且只有一个环,不难想到,环上各边都只需经过一次,因此只需求出树的直径为$L1$,则答案为$2(n-1)-(L1-1)$

    最后比较复杂的是$k=2$的情况了。容易发现形成两个环,它们的公共边任然要经过两次,其他环上的边都仅需一次,非环上边还是两次。因此可以在第二次求树的直径之前把第一次树的直径路径上的边权改为$-1$,表示已经是第一个环上的边了。设第二次用DP做出来的树的直径为$L2$,那么第一次答案减掉$(L1-1)$后公共边部分表示经过一次,再减掉$(L2-1)$后,相当于把公共边少走一次的加回来,表示公共边经过两次。

    PS:第一次全是正权值的边,树的直径可以用DFS;第二次由负边,只能用DP

    Code:

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define fr(i,a,b) for(int i=a;i<=b;i++)
    #define fo(i,a,b) for(int i=a;i>=b;i--)
    #pragma GCC optimize(2)
    const int M=100005;
    int n,k,Max,pos,ans;
    int head[M],Next[2*M],ver[2*M],edge[2*M],tot,v[M],d[M],fa[M];
    void add(int x,int y) {
    	Next[++tot]=head[x];
    	head[x]=tot;
    	ver[tot]=y;
    	edge[tot]=1;
    }
    void dfs(int x) {
    	v[x]=1;
    	if(Max<d[x]) {
    		Max=d[x];
    		pos=x;
    	}
    	for(int i=head[x];i;i=Next[i]) {
    		int y=ver[i];
    		if(v[y]) continue;
    		d[y]=d[x]+1;
    		fa[y]=i; //记录y点是从那条边过来的
    		dfs(y);
    	}
    }
    void dp(int x) {
    	v[x]=1;
    	for(int i=head[x];i;i=Next[i]) {
    		int y=ver[i],z=edge[i];
    		if(v[y]) continue;
    		dp(y);
    		Max=max(Max,d[x]+d[y]+z);
    		d[x]=max(d[x],d[y]+z);
    	} //d[x]表示以x为根的子树到叶子的最长距离
    }
    int main(){
    	std::ios::sync_with_stdio(false);
    	tot=1;
    	scanf("%d%d",&n,&k);
    	fr(i,1,n-1) {
    		int x,y;
    		scanf("%d%d",&x,&y);
    		add(x,y);
    		add(y,x);
    	}
    	dfs(1);
    	int x=pos;
    	d[x]=fa[x]=0;
    	Max=0;
    	memset(v,0,sizeof(v));
    	dfs(x);
    	int y=pos;
    	ans=(n-1)*2-Max+1;
    	if(k==2) {
    		while(fa[y]) { //上一次树的直径的边 边权取反
    			edge[fa[y]]=edge[fa[y]^1]=-1;
    			y=ver[fa[y]^1]; //对称边的终点就是下一个顶点
    		}
    		memset(v,0,sizeof(v));
    		memset(d,0,sizeof(d));
    		Max=0;
    		dp(1);
    		ans-=Max-1;
    	}
    	printf("%d\n",ans);
    	return 0;
    }
  • 相关阅读:
    Javascript构造函数的继承
    什么数据库能抗住《王者荣耀》的1亿DAU?
    支持微信支付亿级请求的TBase数据库大揭秘
    我在MySQL的那些年(一)
    谁是银行核心数据库的破局者?
    X侦探所事件薄 | 一次内存溢出之谜
    腾讯云数据库新生代产品获国家级认证
    POJ 2594 传递闭包的最小路径覆盖
    POJ 1719 二分图最大匹配(记录路径)
    HDU 1533 KM算法(权值最小的最佳匹配)
  • 原文地址:https://www.cnblogs.com/shaozhihang/p/15607179.html
Copyright © 2011-2022 走看看