2018.11.03 练习赛 T3 [codeforces Round #427 Div2.F Roads in the Kingdom]-[树DP+Tarjan缩点]

T3 codeforces Round #427 Div2.F Roads in the Kingdom

题面:

题目描述

给定一个有 \(n\) 个点, \(n\) 条边的图,保证无重边无自环,且图中任意两点均连通,现在请你从图中删掉一条边,且保证剩下的图仍然满足任意两点均连通。

请你求出所有可能的删边方案中,剩下的图中距离最远的两点的距离的最小值。

注意点的编号为从 \(1\) 开始的连续 \(n\) 个数。

输入格式

第一行一个整数 \(n\)

接下来 \(n\) 行,每行三个整数 \(x_i,y_i,z_i\) ,表示 \(x_i\)\(y_i\) 之间有一条长为 \(z_i\) 的边。

输出格式

一行一个整数,表示答案。

样例输入1
3
1 2 4
2 3 5
1 3 1
样例输出1
5
样例输入2
5
2 3 7
3 1 9
4 1 8
3 5 4
4 5 5
样例输出2
18
数据范围

对于 \(30\%\) 的数据: \(n\leq5000\)

对于 \(60\%\) 的数据: \(n\leq10^5\)

对于 \(100\%\) 的数据: \(n\leq10^6,0\leq z_i\leq10^9\)

数据没有梯度


题解:

首先看到这道题很容易想到缩点吧=_=;

然后很容易想到在环上删边吧=_=;

再然后很容易想到跟直径有关吧=_=;

然而这个直径显然是有两种可能的,一种是不经过环,一种是经过环;

缩点之后很显然变成了一棵树,我们以每个环上的点为根求出这个点挂在外面的最长路径长度,(类似[[SDOI]消防]不能经过直径的想法),如果不能经过环,那么最长的一定就是这些外向树的直径中的最大值;

然后考虑经过环内的直径;

在之前计算外向树时,我们将每个外挂点的点权置为可延伸的最长距离;

将环抽离出来放入一个栈方便我们处理;

设环上编号为\([1-m]\),然后假设断掉\([x,x+1]\)这条边就有三种产生贡献的可能,分别是:

1.\(1~x\)中某点的点权加环内的一段边

2.\(x+1~m\)的中某点的点权加环内一段边

3.从断开的两边各选一个点,然后这两个点点权加上环内一段;

处理前两项,我们记录\(Premx[x],Sufmx[x],\)表示\(1~x\)的不经过\(1-m\)这条边的长度最大值,以\(Pre\)为例,转移方程为:

\(Premx[x]=max\{Premx[x-1],max{max\{sumedge}+w[x]\}\}\),

\(sumedge\)记录边权前缀和;

处理第三项:

同样记录前缀/后缀最大值,然后转移,以Pre[x]为例:

\(Pre[x]=max\{Pre[x-1],sumw+w[i]\}\)

\(sumw=max\{sumw,w[i]\}+dis[x]\)

\(code\):

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<stack>
#include<ctype.h>
#include<vector>
#define ld double
#define ls p<<1
#define rs p<<1|1
#define mid (l+r>>1)
#define ll long long
using namespace std;

char buf[1<<20],*p1,*p2;
inline char gc()
{
//	return getchar();
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin))==p1?0:*p1++;
}

template<typename T>
inline void read(T &x)
{
	char tt;
	bool flag=0;
	while(!isdigit(tt=gc())&&(tt!='-'));
	tt=='-'?(flag=1,x=0):(x=(tt^'0'));
	while(isdigit(tt=gc())) x=(x<<1)+(x<<3)+(tt^'0');
	if(flag) x=-x;
}

const int maxn=1e6+2;
struct node{
	int x;
	ll len;
	inline node(int a=0,ll b=0)
	{x=a,len=b;}
};

vector<node>G[maxn];
vector<node>g[maxn];
int low[maxn],dfn[maxn],tot,scc,root,n;
ll dis[maxn],mxdis,mx;
int lb,rb;
int s[maxn],top,head,need[maxn];
ll w[maxn];
ll premx[maxn],sufmx[maxn],pre[maxn],suf[maxn];
bool instack[maxn];

void dfs_(int x,int pre)
{
	low[x]=dfn[x]=++tot;
	s[++top]=x;instack[x]=1;ll p;
	for(ll i=G[x].size()-1;i>=0;i--)
	{
		p=G[x][i].x;
		if(p==pre) continue;
		if(!dfn[p]) dfs_(p,x),
		low[x]=min(low[x],low[p]);
	 	else if(dfn[p]&&instack[p])
	 	low[x]=min(low[x],low[p]);
	}
	if(low[x]==dfn[x])
	{
		bool flag=0;
		scc++;
		do{
			p=s[top--];
			instack[p]=0;
			if(x^p)
			{
				flag=1;
				root=scc;
				need[++head]=p;
			}
		}while(x^p);
		if(flag) need[++head]=x;
	}
}

ll dfs(int x,int pre)
{
	ll fir=0,sec=0,thd;
	for(int i=G[x].size()-1;i>=0;i--)
	{
		int p=G[x][i].x;
		ll len=G[x][i].len;
		if(p==pre or p==lb or p==rb) continue;
		thd=dfs(p,x)+len;
		if(thd>=fir)
		{
			sec=fir;
			fir=thd;
		}
		else if(thd>sec)
		sec=thd;
	}
	mx=max(mx,fir+sec);
	return fir;
}

int main()
{
	read(n);
	for(int i=1;i<=n;i++)
	{
		int x,y;
		ll z;
		read(x),read(y),read(z);
		G[x].push_back(node(y,z));
		G[y].push_back(node(x,z));
	}
	for(int i=1;i<=n;i++)
	if(!dfn[i]) dfs_(i,0);
	while(head)
	{
		s[++top]=need[head];
		head--;
	}
	s[0]=s[top];
	s[top+1]=s[1];
	for(int x=1;x<=top;x++)
	{
		lb=s[x-1],rb=s[x+1];
		w[x]=dfs(s[x],0);
		for(int i=G[s[x]].size()-1;i>=0;i--)
		{
			int p=G[s[x]][i].x;
			ll len=G[s[x]][i].len;
			if(p^s[x-1]) continue;
			dis[x-1]=len;
		}
	}
	dis[top]=dis[0];
	w[0]=w[top];
	w[top+1]=w[1];
	ll sumedge=0,sumw=0;
	for(int i=1;i<=top;i++)
	{
		pre[i]=max(pre[i-1],sumedge+w[i]);
		premx[i]=max(premx[i-1],sumw+w[i]);
		sumedge+=dis[i];
		sumw=max(sumw,w[i])+dis[i];
	}
	sumedge=0,sumw=0;
	for(int i=top;i>=1;i--)
	{
		suf[i]=max(suf[i+1],sumedge+w[i]);
		sufmx[i]=max(sufmx[i+1],sumw+w[i]);
		sumedge+=dis[i-1];
		sumw=max(sumw,w[i])+dis[i-1];
//		printf("%d ",sumw);
	}
	ll ans=premx[top];
	for(ll i=1;i<top;i++)
	ans=min(ans,max(pre[i]+suf[i+1]+dis[0],max(premx[i],sufmx[i+1])));
	printf("%lld",max(mx,ans));
//	for(int i=0;i<=top+1;i++)
//	printf("%lld %lld\n",mx,mx);
}

posted @ 2018-11-03 23:15  Katoumegumi  阅读(68)  评论(0编辑  收藏  举报
返回顶部