基环树专题

基环树专题

最近公共祖先

A c w i n g   392 Acwing~392 Acwing 392

const int MaxN=5e5+1e4;

int Head[MaxN],Dp[MaxN][21],Belong[MaxN],Dad[MaxN];
inline int GetDad(int x){
	return x==Dad[x]?x:Dad[x]=GetDad(Dad[x]);
}
inline void Union(int x,int y){
	x=GetDad(x);y=GetDad(y);
	if(x!=y)Dad[x]=y;
}
bool Visit[MaxN],InSta[MaxN];
int Root[MaxN],Dep[MaxN],Sum[MaxN];
inline void Dfs(int from){
	Visit[from]=InSta[from]=true;
	if(InSta[Dp[from][0]]==true){
		int cnt=0,now=Dp[from][0];
		while(true){
			Root[now]=now;Dep[now]=0;Head[now]=from;
			InSta[now]=false;Sum[now]=++cnt;
			if(now==from)break;
			now=Dp[now][0];
		}
		return;
	}
	if(!Visit[Dp[from][0]])Dfs(Dp[from][0]);
	if(!Root[from]){
		Root[from]=Root[Dp[from][0]];
		Dep[from]=Dep[Dp[from][0]]+1;
		InSta[from]=false;
	}
	for(register int i=1;i<=20;i++)
		Dp[from][i]=Dp[Dp[from][i-1]][i-1];
}

inline int GetLca(int x,int y){
	if(Dep[x]<Dep[y])Swap(x,y);
	for(register int i=20;i>=0;i--)
		if(Dep[Dp[x][i]]>=Dep[y])
			x=Dp[x][i];
	if(x==y)return x;
	for(register int i=20;i>=0;i--)
		if(Dp[x][i]!=Dp[y][i])
			x=Dp[x][i],y=Dp[y][i];
	return Dp[x][0];
}
int n,q;
int main(){
	scanf("%d%d",&n,&q);
	for(register int i=1;i<=n;i++)
		Dad[i]=i;
	for(register int i=1;i<=n;i++)
		scanf("%d",&Dp[i][0]),Union(i,Dp[i][0]);
	for(register int i=1;i<=n;i++)
		if(!Visit[i])Dfs(i);
	for(register int Test=1,x,y;Test<=q;Test++){
		scanf("%d%d",&x,&y);
		if(GetDad(x)!=GetDad(y))puts("-1 -1");
		else if(Root[x]==Root[y]){
			int p=GetLca(x,y);
			printf("%d %d\n",Dep[x]-Dep[p],Dep[y]-Dep[p]);
		}
		else{
			int a=Dep[x],b=Dep[y],Cx,Cy;
			Cx=abs(Sum[Root[x]]-Sum[Root[y]]);
			Cy=Sum[Head[Root[x]]]-Cx;
			if(Sum[Root[x]]<Sum[Root[y]])Swap(Cx,Cy);
			if(Max(Cx+a,b)<Max(a,Cy+b)||(Max(Cx+a,b)==Max(a,Cy+b)&&(Min(a+Cx,b)<Min(a,b+Cy)||(Min(a+Cx,b)==Min(a,b+Cy)&&a+Cx>=b))))
				printf("%d %d\n",Cx+a,b);
			else printf("%d %d\n",a,b+Cy);
		}
	}
	return 0;
} 

直径

const int MaxN=1e6+1e5;
const int MaxM=2e6+1e5;
Graphs<long long,MaxN,MaxM>G;
bool Visit[MaxN],Mark[MaxN],flag;
int Pre[MaxN];
vector<Pairs<int,long long> >Ring;
inline void GetRing(int from,int last){
	Visit[from]=true;
	for(register int k=G.Last[from];k!=-1;k=G.Next[k]){
		if((k^1)==last)continue;
		if(flag&&Visit[G.To[k]])continue;
		if(Visit[G.To[k]]){
			int now=k^1;
			do{
				Ring.push_back(Pairs<int,long long>(G.To[now],G.Cost[now]));
				Mark[G.To[now]]=true;now=Pre[G.To[now]];
			}while(G.To[k]!=G.To[now]);
			Ring.push_back(Pairs<int,long long>(G.To[now],G.Cost[now]));
			Mark[G.To[now]]=true;flag=true;
			continue;
		} 
		Pre[G.To[k]]=k^1;
		GetRing(G.To[k],k);
	}
}
long long res,Dist[MaxN];
inline void Dfs(int from,int dad){
	for(register int k=G.Last[from];k!=-1;k=G.Next[k]){
		if(G.To[k]==dad||Mark[G.To[k]])continue;
		Dfs(G.To[k],from);
		res=Max(res,Dist[from]+Dist[G.To[k]]+G.Cost[k]);
		Dist[from]=Max(Dist[from],Dist[G.To[k]]+G.Cost[k]);
	}
}
int Queue[MaxM],head,tail;
long long Arr[MaxM],Sum[MaxM];
inline void Solve(){
	int sizer=Ring.size();
	for(register int i=0;i<sizer;i++){
		Dfs(Ring[i].x,0);
		Arr[i]=Dist[Ring[i].x];
		Sum[i]=Ring[i].y;
	}
	memcpy(Arr+sizer,Arr,sizer*sizeof(long long));
	memcpy(Sum+sizer,Sum,sizer*sizeof(long long));
	head=tail=0;
	for(register int k=0;k<2*sizer;k++){
		Sum[k]+=Sum[k-1];
		while(head!=tail&&k-Queue[head]>=sizer)head++;
		if(head!=tail)
			res=Max(res,Arr[Queue[head]]+Arr[k]+Sum[k]-Sum[Queue[head]]);
		while(head!=tail&&Arr[k]-Sum[k]>=Arr[Queue[tail-1]]-Sum[Queue[tail-1]])tail--;
		Queue[tail++]=k;
	}
}
inline void ReInit(){
	flag=false;
	Ring.clear();
	res=0;
}
int n;
int main(){
	Read(n);
	for(register int i=1,x,y;i<=n;i++){
		Read(x,y);
		Insert(G,i,x,1ll*y);Insert(G,x,i,1ll*y);
	}
	long long ans=0;
	for(register int i=1;i<=n;i++){
		if(Visit[i])continue;
		ReInit();
		GetRing(i,Inf);
		Solve();
		ans+=res;
	}
	printf("%lld",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值