[Luogu P4230]连环病原体

题目大意

(n)个点,(m)条边,对每条边进行编号,若编号为([l,r])的区间内出现了环,则给每一条边的(val)(1),最后输出每条边的(val)

大体思路

注意到:若([l,r])之间存在环,则区间([l,r])到区间([l,m])都包含有环。

再整理一下得到:

  • 给区间([l,r])中的边,每条边的(val)都加上"(m-r+1)"

  • 给区间([r+1,m])中的边,加上一个首相为"(m-r)",公差为"(-1)"的等差数列。

这个显然可以用二维差分得到。

那么现在的问题就是:如何找到又环的区间?

很简单,我们可以用双指针,因为随着 (l) 的增大,(r) 必然不会减小。

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=400005;
struct edge{
	int u,v;
} e[N];
ll delta1[N],delta2[N];
int val[N],fa[N],rev[N],ch[N][2];
int m,q,opt;
int wh(int x){return ch[fa[x]][1]==x;}
bool isrt(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
void rever(int x){rev[x]^=1,swap(ch[x][0],ch[x][1]);}
void pushdown(int x){
	if (rev[x]){
		if (ch[x][0]) rever(ch[x][0]);
		if (ch[x][1]) rever(ch[x][1]);
		rev[x]=0;
	}
}
void Allpushdown(int x){
	if (!isrt(x)) Allpushdown(fa[x]);
	pushdown(x);
}
void rotate(int x){
	int y=fa[x],z=fa[y],c=wh(x);
	if (!isrt(y)) ch[z][wh(y)]=x;
	fa[x]=z;
	ch[y][c]=ch[x][c^1];
	fa[ch[y][c]]=y;
	ch[x][c^1]=y;
	fa[y]=x;
}
void splay(int x){
	Allpushdown(x);
	for (;!isrt(x);rotate(x))
		if (!isrt(fa[x])) rotate(wh(fa[x])==wh(x)?fa[x]:x);
}
void access(int x){
	for (int y=0;x;y=x,x=fa[x]) splay(x),ch[x][1]=y;
}
void makert(int x){
	access(x),splay(x),rever(x);
}
int findrt(int x){
	access(x),splay(x);
	while (ch[x][0]) pushdown(x),x=ch[x][0]; 
	splay(x);
	return x;
}
void link(int x,int y){
	makert(x),fa[x]=y;
}
void cut(int x,int y){
	makert(x),access(y),splay(y);
	if (ch[y][0]!=x&&!ch[ch[y][0]][1]) return;
	fa[x]=ch[y][0]=0;
}
void query(int x,int y){
	makert(x),access(y),splay(y);
}
void solve(int l,int r,ll head,ll delta){
	if (l>r) return;
	delta1[l]+=head;
	delta1[r+1]+=-head;
	delta2[l+1]+=delta;
	delta2[r+1]+=-delta;
}
int main(){
	scanf("%d",&m);
	for (int i=1;i<=m;i++) scanf("%d%d",&e[i].u,&e[i].v);
	int r=1;
	for (int l=1;l<=m;l++){
		while (r<=m){
			int u=e[r].u,v=e[r].v;
			if (findrt(u)==findrt(v)){
				solve(l,r,m-r+1,0);
				solve(r+1,m,m-r,-1);
				break;
			} else link(u,v);
			++r;
		}
		cut(e[l].u,e[l].v);
	}
	ll x=0,y=0;
	for (int i=1;i<=m;i++){
		x+=delta1[i],y+=delta2[i],x+=y;
		printf("%lld ",x);
	}
	puts("");
	return 0;
}
原文地址:https://www.cnblogs.com/WR-Eternity/p/10899341.html