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