luogu P4381 [IOI2008]Island

题面传送门
这是一道基环树直径的题目。
考虑对于一个基环树,只有两种情况:直径在环上,直径在半个环上与链上。
直径在环上直接搞就好了,另一种情况可以单调队列优化dp
代码实现:

#include<cstdio>
#include<cstring>
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;
int n,m,k,x,y,z,st,flag[1000039],flag2[1000039],q[2000039],head,tail,f[1000039],fh;
long long ans,dp[1000039],tot,sf[1000039],pus,d[1000039];
struct yyy{int to,w,z;}tmp;
struct ljb{
	int head,h[1000039];
	yyy f[2000039];
	inline void add(int x,int y,int z){
		f[++head]=(yyy){y,z,h[x]};
		h[x]=head;
	}
}s;
inline int dfs(int x,int last){
	if(flag[x]==1){
		flag[x]=2;f[++fh]=x;flag2[x]=1;
		return 1;
	}
	flag[x]=1;
	int cur=s.h[x];
	yyy tmp;
	while(cur!=-1){
		tmp=s.f[cur];
		if(cur!=((last-1)^1)+1&&dfs(tmp.to,cur)) {
			if(flag[x]!=2)f[++fh]=x,flag2[x]=1,sf[fh]=sf[fh-1]+tmp.w;
			else{sf[st-1]=sf[st]-tmp.w;return 0;}
			return 1;
		}
		cur=tmp.z;
	}
	return 0;
}
inline void tdp(int x){
	flag2[x]=1;
	int cur=s.h[x];
	yyy tmp;
	while(cur!=-1){
		tmp=s.f[cur];
		if(!flag2[tmp.to]) {
			tdp(tmp.to);
			pus=max(pus,d[x]+d[tmp.to]+tmp.w);
			d[x]=max(d[x],d[tmp.to]+tmp.w);
		}
		cur=tmp.z;
	}
}
inline void read(int &x){
	char s=getchar();x=0;
	while(s<'0'||s>'9') s=getchar();
	while(s>='0'&&s<='9') x=(x<<3)+(x<<1)+(s^48),s=getchar();
}
int main(){
	memset(s.h,-1,sizeof(s.h));
	register int i,j;
	scanf("%d",&n);
	for(i=1;i<=n;i++) read(x),read(z),s.add(x,i,z),s.add(i,x,z);
	for(i=1;i<=n;i++){
		if(!flag2[i]){
			st=fh+1;
			dfs(i,0);
			tot=0;
			for(j=st;j<=fh;j++){
				pus=0;
				tdp(f[j]);
				tot=max(tot,pus);
				dp[j+fh-st+1]=dp[j]=d[f[j]];
				sf[j+fh-st+1]=sf[j+fh-st]+sf[j]-sf[j-1];
			}
			head=tail=0;pus=0;
			for(j=st;j<=2*fh-st+1;j++){
				while(head!=tail&&q[head+1]<=j-fh+st-1) head++;
				if(head!=tail)pus=max(pus,dp[j]+dp[q[head+1]]+sf[j]-sf[q[head+1]]);
				while(head!=tail&&dp[q[tail]]-sf[q[tail]]<=dp[j]-sf[j]) tail--;
				q[++tail]=j; 
			}
			ans+=max(pus,tot);
			//printf("%lld %lld\n",tot,pus);
		}
	}
	printf("%lld\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值