题解:HDU 7436
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);
}

浙公网安备 33010602011771号