Loading

CF835F Roads in the Kingdom/P1399 [NOI2013]快餐店

CF835F Roads in the Kingdom

P1399 [NOI2013]快餐店

真好玩,CF考NOI原题。如果您看懂了题目的话发现这两题只有两个差别:1. 数据范围;2. 第一题的答案除以2保留一位小数就是第二题答案。成功双倍经验

开始讲做法

首先按照套路找环,记录环上权值,同时对于每一个环上的点跑出它子树内的最长链 \(f_i\)

话说我看到了一个比较方便的找环同时记录环上的方法,比我在 P4381 [IOI2008]Island 里头自己yy的屎山代码好多了,就学了一下。

接下去发掘一下性质。

首先,断完边原图仍然联通,那么必然是一颗树,必然只能断环上的边

显然枚举一下断哪条边

then?

P4381 [IOI2008]Island 一样把答案分为两部分。

每个子树内的最长链最大值设为 \(ans1\)

经过环上两个点的最长链设为 \(ans2\) ,难点还是在于求 \(ans2\)

最终答案还是 \(\max(ans1,ans2)\) 真·套路

环是很难搞的,还是拍到序列上。

首先,必然需要选两个端点 \(l,r(l<r)\) ,它们所能达到的最长长度就是 \(f_l+f_r+dis_{l,r}\)

根据套路转化为前缀和之差

??如果认真考虑会发现这里不那么套路了

断了一条边之后有可能 \(sum_r-sum_l\) 或者是 \(sum_n-(sum_r-sum_l)\) 不能达到 ,因为中间有条边断掉了。

那么就分类讨论!

设断的边是 \(i-1\to i\)

  • \(l<r\le i-1\) ,记 \(ll_{r}\) 表示这种情况下,右端点为 \(r\) 时直径的最大值。注意这里应该求最大值,因为我们求的是断完边后的直径\(ll_{r}=\max\{f_r+sum_r+f_l-sum_l\}\) ,从前往后扫同时维护 \(\max\{f_l-sum_l\}\) 即可求出
  • \(i\le l<r\) ,同上,记 \(rr_{l}\) 表示这种情况下,左端点为 \(l\) 时直径的最大值。\(rr_l=\max\{f_r+sum_r+f_l-sum_l\}\) ,从后往前扫同时维护 \(\max\{f_r+sum_r\}\)
  • \(l\le i-1,i\le r\) ,那么必然“绕”过了断的边,\(ans=\min\{f_l+sum_l+f_r+sum_{cnt-sum_r} \}\) ,维护 \(L_i=\max\{f_l+sum_l\},R_i=\max\{f_r+sum_{cnt}-sum_r\}\)\(ans=\min\{L_{i-1}+R_i\}\)

整合一下就完事了。

我现在对于基环树的感觉就是边界好烦啊,比如这里的 \(dis_{l,r}=sum_r-sum_l\) ,边界加一减一还是不变,我得要输出一下,看看程序跑成啥样才能知道,有没有dalao有好的方法,指点一下蒟蒻吧!

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef double db;
#define x first
#define y second
#define sz(v) (int)v.size()
#define pb(x) push_back(x)
#define mkp(x,y) make_pair(x,y)
inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=0;c=getchar();}
	while(isdigit(c))x=x*10+c-'0',c=getchar();
	return f?x:-x;
}
#define N 200005
#define inf 1000000000000000ll
int n;
int loop[N],cnt,val[N],vis[N];
LL sum[N],f[N],ans1,ans2,L[N],R[N],ll[N],rr[N];
int head[N],num_edge;
struct edge{int nxt,to,val;}e[N<<1];
void addedge(int fr,int to,int val){
	++num_edge;
	e[num_edge].nxt=head[fr];
	e[num_edge].to=to;
	e[num_edge].val=val;
	head[fr]=num_edge;
}
int dfs(int u,int ft){
	if(vis[u])return u;
	vis[u]=1;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;if(v==ft)continue;
		int tmp=dfs(v,u);
		if(tmp){
			loop[++cnt]=u,val[cnt]=e[i].val,vis[u]=2;
			return tmp==u?0:tmp;
		}
	}
	return 0;
}
void getd(int u,int ft){
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;if(v==ft||vis[v]==2)continue;
		getd(v,u);
		ans1=max(ans1,f[v]+f[u]+e[i].val);
		f[u]=max(f[u],f[v]+e[i].val);
	}
}
signed main(){
	n=read();
	for(int i=1;i<=n;++i){
		int x=read(),y=read(),z=read();
		addedge(x,y,z),addedge(y,x,z);
	}
	dfs(1,0);
	for(int i=1;i<=cnt;++i)getd(loop[i],0);
	for(int i=1;i<=cnt;++i)sum[i]=sum[i-1]+val[i];
	LL tmp;
	L[0]=ll[0]=R[cnt+1]=rr[cnt+1]=-inf;
	tmp=-inf;
	for(int i=1;i<=cnt;++i){
		L[i]=max(L[i-1],f[loop[i]]+sum[i]);
		ll[i]=max(ll[i-1],f[loop[i]]+sum[i]+tmp);
		tmp=max(tmp,f[loop[i]]-sum[i]);
	}//ll[r]=max{f[r]+sum[r]+f[l]-sum[l]}
	tmp=-inf;
	for(int i=cnt;i>=1;--i){
		R[i]=max(R[i+1],f[loop[i]]+sum[cnt]-sum[i]);
		rr[i]=max(rr[i+1],f[loop[i]]-sum[i]+tmp);
		tmp=max(tmp,f[loop[i]]+sum[i]);
	}//rr[l]=max{f[r]+sum[r]+f[l]-sum[l]}
	//both:ans=min{f[l]+sum[l]+f[r]+sum[cnt]-sum[r]}
	ans2=inf;
	for(int i=1;i<=cnt;++i)ans2=min(ans2,max(L[i-1]+R[i],max(ll[i-1],rr[i])));
	printf("%lld\n",max(ans1,ans2));
	return 0;
}

对了,我还从CF上以某种神奇手段把Test#4搞了下来(((

https://paste.ubuntu.com/p/XD72QGngD3/

posted @ 2020-10-26 22:16  zzctommy  阅读(112)  评论(0编辑  收藏  举报