zoukankan      html  css  js  c++  java
  • 洛谷P3761 [TJOI2017]城市

    题面:https://www.luogu.com.cn/problem/P3761
    一句话题意:给一棵有边权的树,删一条边并加一条等权边,最小化新树的直径。
    题解:
    考虑先两边DFS求出树的直径。(不会的请自行百度)
    那么我们要删的边一定是直径的某一条边。
    证明:如果断的不是直径上的边,那么新直径一定(geq)原直径。
    既然题目给了(n^2)的时限,我们就可以枚举删的是哪一条边。
    现在考虑如何连边。
    删边后,我们得到了两棵树。那么新的直径可能有三种情况:
    A树上的一条链,B树上的一条链,或是A、B合并后经过新边的一条链。
    前两个我们可以(O(n))求出。至于第三个,我们类似找树的重心,
    ----也就是树上离这个点最远距离最小的点。这个可以两边DFS,
    第一次记录下每个点的子树距离最大值和次大值,
    第二次换根搞一下就好。
    时间复杂度O((n^2))
    (O(n))实在太巨了,请翻看其他题解
    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define re register int
    #define F(x,y,z) for(re x=y;x<=z;x++)
    #define FOR(x,y,z) for(re x=y;x>=z;x--)
    typedef long long ll;
    #define I inline void
    #define IN inline int
    template<class D>I read(D &res){
    	res=0;register D g=1;register char ch=getchar();
    	while(!isdigit(ch)){
    		if(ch=='-')g=-1;
    		ch=getchar();
    	}
    	while(isdigit(ch)){
    		res=(res<<3)+(res<<1)+(ch^48);
    		ch=getchar();
    	}
    	res*=g;
    }
    const int INF=1e9+7;
    struct E{
    	int to,nt,w;
    }e[10100];
    #define T e[k].to
    int n,m,tot,X,Y,W,head[5050],ans,mx[2][5050],d[5050],f[5050];
    vector<int>pa;
    I add(int x,int y){
    	e[++tot].to=y;
    	e[tot].nt=head[x];
    	head[x]=tot;
    	e[tot].w=W;
    }
    I D_1(int x,int fa,int dis){
    	if(dis>m){m=dis;X=x;}
    	for(re k=head[x];k!=-1;k=e[k].nt){
    		if(T==fa||k==W||(k^1)==W)continue;
    		D_1(T,x,dis+e[k].w);
    	}
    }
    I D_2(int x,int fa,int dis){
    	d[x]=dis;
    	if(dis>m){m=dis;Y=x;}
    	for(re k=head[x];k!=-1;k=e[k].nt){
    		if(T==fa||k==W||(k^1)==W)continue;
    		D_2(T,x,dis+e[k].w);
    	}
    }
    I getpath(int x,int fa){
    	if(x==Y){m=1;return;}
    	for(re k=head[x];k!=-1;k=e[k].nt){
    		if(T==fa||k==W||(k^1)==W)continue;
    		getpath(T,x);
    		if(m){
    			//cout<<"!"<<x<<endl;
    			pa.emplace_back(k);return;
    		}
    	}	
    }
    int mn[2];
    I D_3(int x,int fa){
    	re dis;
    	for(re k=head[x];k!=-1;k=e[k].nt){
    		if(T==fa||k==W||(k^1)==W)continue;
    		D_3(T,x);
    		dis=mx[0][T]+e[k].w;
    		if(dis>mx[0][x])mx[1][x]=mx[0][x],mx[0][x]=dis;
    		else if(dis>mx[1][x])mx[1][x]=dis;
    	}
    }
    I D_4(int x,int fa,int dis,int s){
    	if(dis>mx[0][x])mx[1][x]=mx[0][x],mx[0][x]=dis;
    	else if(dis>mx[1][x])mx[1][x]=dis;
    	//cout<<x<<":"<<mx[0][x]<<" "<<mx[1][x]<<endl;
    	mn[s]=min(mn[s],mx[0][x]);
    	for(re k=head[x];k!=-1;k=e[k].nt){
    		if(T==fa||k==W||(k^1)==W)continue;
    		D_4(T,x,(mx[0][T]+e[k].w==mx[0][x]?mx[1][x]:mx[0][x])+e[k].w,s);
    	}
    }
    IN get_diameter(int x,int y,int val){
    	//cout<<x<<" "<<y<<" "<<val<<" ";
    	re res=INF;memset(mx,0,sizeof(mx));mn[0]=mn[1]=INF;
    	D_3(x,0);D_4(x,0,0,0);//cout<<mn<<endl;
    	m=0;D_1(x,0,0);m=0;D_2(X,0,0);res=d[Y];//cout<<mn[0]<<" "<<d[Y]<<" ";
    	D_3(y,0);D_4(y,0,0,1);
    	m=0;D_1(y,0,0);m=0;D_2(X,0,0);res=max(res,max(d[Y],mn[0]+mn[1]+val));
    	//cout<<mn[1]<<" "<<d[Y]<<" "<<mn[0]+mn[1]+val<<endl;
    	return res;
    }
    int main(){
    	read(n);tot=-1;
    	memset(head,-1,sizeof(head));
    	F(i,1,n-1){
    		read(X);read(Y);read(W);
    		add(X,Y);add(Y,X);
    	}
    	m=0;W=-2;D_1(1,0,0);m=0;D_2(X,0,0);
    	//cout<<X<<" "<<Y<<endl;
    	m=0;getpath(X,0);
    	if(n==1){printf("0");return 0;}
    	ans=INF;
    	for(auto p:pa){
    		//cout<<e[p].to<<" "<<e[p^1].to<<":"<<endl;
    		W=p;ans=min(ans,get_diameter(e[p].to,e[p^1].to,e[p].w));
    		//cout<<ans<<endl;
    	}
    	printf("%d",ans);
    	return 0;
    }
    
  • 相关阅读:
    [收藏]15分钟内快速构建数据访问层
    asp.net 水晶报表主从表关联问题
    C#基础——关于类
    C#和Sql的时间操作心得(一)
    DataGrid导出到Word/Excel文档
    [收藏]SQL Server 索引结构及其使用
    .NET环境下水晶报表使用总结
    读写文件之日志文件
    HashTable实现购物车,抛弃数据库实现方式
    触碰心灵34句
  • 原文地址:https://www.cnblogs.com/Purple-wzy/p/12091825.html
Copyright © 2011-2022 走看看