JZOJ 5905. 【NOIP2018模拟10.15】黑暗之魂

题意

求一棵树/一个环套树的两点最短距离的最大值。
//和图的直径有所不同

解题思路

代码繁琐。思想简单。比赛时没拍。
求图的直径。
环套树,记dis(x,y)为环上的点x到点y的最短路径。
显然,给环定一个方向,求个前缀和。对于每个环上的点x,必有一个a[x],表示按照给定的方向走,第一个满足环长len-(sum[a[x]]-sum[x])<sum[a[x]]-sum[x]
a[x]显然不递减。
所以拿个线段树维护一下。一个区间加,一个区间减。
据说还可以单调队列, O ( n ) O(n) O(n)做。

代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define N 1000005
#define LL long long 
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
struct Note{
	int x,y;
	LL z;
}c[N];
struct note{
	int to,next,val;
}edge[N<<1];
struct sgm{
	LL la,mx;
}tr[N*12];
int Tot,head[N];
int i,j,k,l,n,m,gx,gy;
int opl,opr,L1,R1;
LL opz;
LL mx[N],cmx[N],E[N*3];
int dep[N];
int fa[N],o[N],o1[N];
int T,tot,cnt,opx;
int L2,R2;
int f[N],g[N];
int A,B;
LL ans,cd,C,D1,cd1;
bool bz[N];
int read(){
	int rs=0,fh=1;char ch;
	while((ch<'0'||ch>'9')&&(ch^'-'))ch=getchar();
	if(ch=='-')fh=-1,ch=getchar();
	while(ch>='0'&&ch<='9')rs=(rs<<3)+(rs<<1)+(ch^'0'),ch=getchar();
	return rs;
}
int get(int x){return f[x]==x?x:f[x]=get(f[x]);}
void lb(int x,int y,int z){
	edge[++Tot].to=y;
	edge[Tot].next=head[x];
	edge[Tot].val=z;
	head[x]=Tot;
}
void dg(int x){
	int i;
	for(i=head[x];i;i=edge[i].next)
	    if(edge[i].to^fa[x]){
	    	g[edge[i].to]=edge[i].val;
	    	fa[edge[i].to]=x;
	    	dep[edge[i].to]=dep[x]+1;
	    	dg(edge[i].to);
		}
}
void dg1(int x){
	int i;
	for(i=head[x];i;i=edge[i].next)
	    if((edge[i].to^fa[x])&&!bz[edge[i].to]){
	    	fa[edge[i].to]=x;
		    dg1(edge[i].to);
		    C=mx[edge[i].to]+edge[i].val;
		    if(C>=mx[x])cmx[x]=mx[x],mx[x]=C;
		    else
		    if(C>=cmx[x])cmx[x]=C;
	    }
}
int getlca(int A,int B){
	int i,A1=A,B1=B;
	if(dep[A1]<dep[B1])swap(A1,B1);
	while(dep[A1]>dep[B1])A1=fa[A1];
	if(A1==B1)return A1;
	while(A1^B1)A1=fa[A1],B1=fa[B1];
	return A1;
}
void chuli(){
	int i,A1=A,B1=B;
	tot=dep[A]+dep[B]-2*dep[C]+1;
	if(dep[A1]<dep[B1])swap(A1,B1);
	while(dep[A1]>dep[B1]){
	    cnt++;
	    o[cnt]=A1;
	    o1[cnt+1]=g[A1];
		A1=fa[A1];
	}
	if(A1==B1){
		o[++cnt]=A1;
		return;
	}
	int D=tot;
	while(A1^B1){
		cnt++;
		o[cnt]=A1;
		o1[cnt+1]=g[A1];
		A1=fa[A1];
		o[D]=B1;
		o1[D]=g[B1];
		B1=fa[B1];
		D--;
	}
	o[D]=A1;
}
void update(int ps){
	tr[ps].mx=max(tr[ps<<1].mx,tr[(ps<<1)|1].mx);
}
void downld(int ps){
	if(tr[ps].la!=0){
		tr[ps<<1].mx+=tr[ps].la;
		tr[(ps<<1)|1].mx+=tr[ps].la;
		tr[ps<<1].la+=tr[ps].la;
		tr[(ps<<1)|1].la+=tr[ps].la;
		tr[ps].la=0;
	}
}
void change(int ps,int l,int r){
	if(opl<=l&&r<=opr){
		tr[ps].la+=opz;
		tr[ps].mx+=opz;
		return;
	}
	downld(ps);
	int wz=(l+r)>>1;
	if(opl<=wz)change(ps<<1,l,wz);
	if(opr>wz)change((ps<<1)|1,wz+1,r);
	update(ps);
}
LL query(int ps,int l,int r){
	if(opl<=l&&r<=opr){
		return tr[ps].mx;
	}
	downld(ps);
	LL rs=0;
	int wz=(l+r)>>1;
	if(opl<=wz)rs=max(rs,query(ps<<1,l,wz));
	if(opr>wz)rs=max(rs,query((ps<<1)|1,wz+1,r));
	update(ps);
	return rs;
}
int main(){
    n=read();
    fo(i,1,n){
    	j=read();k=read();l=read();
    	if(j==k)continue;
    	tot=tot+1;
    	c[tot].x=j,c[tot].y=k,c[tot].z=l;
	}
    fo(i,1,n)f[i]=i;
    A=B=0;
    fo(i,1,tot){
    	gx=get(c[i].x);
    	gy=get(c[i].y);
    	if(gx^gy){
    		lb(c[i].x,c[i].y,c[i].z);
    		lb(c[i].y,c[i].x,c[i].z);
    		f[gy]=gx;
		}else{
			A=c[i].x;
			B=c[i].y;
			D1=c[i].z;
		}
	}
	dg(1);
	if(A==0&&B==0){
		dg1(1);
		ans=0;
		fo(i,1,n)ans=max(ans,mx[i]+cmx[i]);
		ans++;
        printf("%lld",ans);
		return 0;
	}
	C=getlca(A,B);
	chuli();
	memset(fa,0,sizeof(fa));
	fo(i,1,tot)bz[o[i]]=1;
	fo(i,1,tot)dg1(o[i]);
	fo(i,1,n)ans=max(ans,mx[i]+cmx[i]);
	L1=1,R1=tot*3-2;
	fo(i,1,tot)cd=cd+o1[i];
	cd=cd+1ll*D1;
	cd1=cd;
	opz=cd;
	fo(i,1,tot-1){
		cd=cd-o1[i+1];
		E[i]=cd;
		opz=cd+mx[o[i+1]];
		opl=opr=i;
		change(1,1,R1);
	}
	cd=0;
	fo(i,tot,tot+tot-1){
		cd=cd+o1[i-tot+1];
		E[i]=cd;
		opz=cd+mx[o[i-tot+1]];
		opl=opr=i;
		change(1,1,R1);
	}
	cd=cd1;
	fo(i,tot*2,tot*3-2){
		cd=cd+o1[i-2*tot+1];
		E[i]=cd;
		opz=cd+mx[o[i-2*tot+1]];
		opl=opr=i;
		change(1,1,R1);
	}
	L1=tot;cd=0;
	fo(i,tot,tot+tot-1){
		cd=E[i];
	    while(L1<=R1){
	    	D1=E[L1]-cd;
	    	if(D1>cd1-D1)break;
			L1++;
		}
		opl=i+1,opr=L1-1;
		ans=max(ans,query(1,1,R1)+mx[o[i-tot+1]]);
		opl=opr-tot+1,opr=i-1;
		ans=max(ans,query(1,1,R1)+mx[o[i-tot+1]]);
		opl=1,opr=i-1;opz=E[i+1]-E[i];
		change(1,1,R1);
		opl=i+1,opr=R1;opz=-(E[i+1]-E[i]);
		change(1,1,R1);
	}
    ans++;
    printf("%lld",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值