题解:HDU 7436

Link

1. Description

给定一张有 \(n\) 个点 \(m\) 条边的图,每一条边有四个参数 \((l,r,a,b)\) 表示这条边将在 \([l,r]\) 时刻存在,连接 \(a,b\) 两个节点,现在对于 \(1\le i\le n\),求出 \(1\)\(i\) 联通的所有时刻之和。

2. Solution

看到一条边在某一时间段内生效的连通性问题,我们第一时间想到线段树分治结合可撤销并查集求解,然后分治到叶子节点时,给 \(1\) 所在的连通块整体加上叶子节点对应的时刻。

现在的问题是,我们怎么给 \(1\) 所在的连通块整体加上一个值?

我们知道,并查集合并之后会得到一个树形结构,不妨类比线段树区间加的思想,使用一个类似于永久化标记的东西去记录,不妨记 \(tag_x\) 表示以 \(x\) 为根的子树整体加了多少,显然我们应当保证一个节点到根整条路径上的 \(tag\) 之和恰好是这个节点增加的值,设为 \(add\)。依据此,考虑合并和撤销操作时对应节点 \(tag\) 的变化。

先考虑合并,假设我们需要将 \(y\) 对应的子树合并到 \(x\) 中,由于我们需要保证 \(y\) 对应的子树中任一节点的 \(add\) 不发生变化,所以我们需要消除 \(tag_x\) 对于 \(y\) 对应的子树中的节点的影响,显然,我们不能通过修改 \(tag_x\),也就是我们只能修改 \(tag_y\),由于最后的 \(add\) 增加了 \(tag_x\),所以我们只需要将 \(tag_y\) 减掉 \(tag_x\) 即可。

考虑撤销,仍然保证 \(add\) 不变,考虑 \(y\) 对应的子树中任一节点的 \(add\) 就是到 \(y\) 的路径上的节点的 \(tag\) 之和加上 \(add_y-tag_y\),所以我们只需要把 \(tag_y\) 修改为 \(add_y\) 即可。

那么只需要在可撤销并查集中实现一个整体加,套一个线段树分治的板子即可。

3. Code

/*by qwer6*/
/*略去缺省源和快读快写*/
const int N=6e5+5;
int n,m,cnt_edge;
ll ans;
struct Edge{
	int u,v,nxt;
}e[N*20];
int head[N<<2];
struct DSU{
	int n,top;
	int fa[N],siz[N],st[N];
	ll tag[N]; 
	void init(int _n){
		n=_n,top=0;
		for(int i=1;i<=n;i++){
			fa[i]=i;
			siz[i]=1;
			tag[i]=0;
		}
	}
	int find(int x){
		if(x==fa[x])return x;
		return find(fa[x]);
	}
	int find(int x,ll &res){
		res+=tag[x];
		if(x==fa[x])return x;
		return find(fa[x],res);
	}
	void merge(int x,int y){
		x=find(x),y=find(y);
		if(x==y)return ;
		if(siz[x]<siz[y])swap(x,y);
		st[++top]=y;
		fa[y]=x;
		tag[y]-=tag[x];
		siz[x]+=siz[y];
	}
	void undo(){
		if(!top)return ;
		int y=st[top--],x=fa[y];
		ll add=0;
		find(y,add);
		tag[y]=add;
		fa[y]=y;
		siz[x]-=siz[y];
	}
	void delet(int T=0){
		while(top>T)
			undo();
	}
}dsu;
#define ls p<<1
#define rs p<<1|1
#define mid (l+r>>1)
void change(int p,int l,int r,int &L,int &R,int &u,int &v){
	if(L<=l&&r<=R){
		e[++cnt_edge]={u,v,head[p]};
		head[p]=cnt_edge;
		return ;
	}
	if(mid>=L)change(ls,l,mid,L,R,u,v);
	if(mid<R)change(rs,mid+1,r,L,R,u,v);
}
void dfs(int p,int l,int r){
	int T=dsu.top;
	for(int i=head[p];i;i=e[i].nxt)
		dsu.merge(e[i].u,e[i].v);
	if(l==r){
		dsu.tag[dsu.find(1)]+=l;
		dsu.delet(T);
		return ;
	}
	dfs(ls,l,mid),dfs(rs,mid+1,r);
	dsu.delet(T);
}
#undef ls
#undef rs
#undef mid
signed main(){
	read(n),read(m);
	dsu.init(n);
	for(int i=1,u,v,l,r;i<=m;i++){
		read(u),read(v),read(l),read(r);
		change(1,1,n,l,r,u,v);
	}
	dfs(1,1,n);
	for(int i=1;i<=n;i++)ans^=dsu.tag[i];
	write(ans);
}
posted @ 2025-04-17 18:44  陈牧九  阅读(83)  评论(0)    收藏  举报