专栏文章

题解:P14411 [JOISC 2015] 道路建设 / Road Development

P14411题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@min4ex5v
此快照首次捕获于
2025/12/01 20:23
3 个月前
此快照最后确认于
2025/12/01 20:23
3 个月前
查看原文

形式化题意

给你 nn 个点,一共 qq 次操作,共计两种操作。
  • 1 u v。若 uuvv 点之间不存在路径,则在两点之间建一条边,边权贡献;否则不将路径上的边权计算在贡献中。
  • 2 u v。若两点不联通,输出 -1,否则输出两点路径上有贡献的边的数量。

思路

先看一会,发现需要判断连通性,所以并查集是肯定少不了的。
画图发现,对于操作一,如果两点之间存在路径,我们并不会删除原来的边,也不会添加新边;如果不存在路径,我们也只会建一条边,两点所属的联通块就会合并,两个联通块的点就存在路径。
所以我们再往下推导,不难发现全部操作完后的图是不存在环的,那不就是吗!
再看操作,有修改和查询,就不难想到用树链剖分来维护。具体来说,操作一就是分两种情况:联通,就将路径贡献变为 00,就是区间覆盖;不联通,就新建边,边权为 11
所以整道题的思路就出来了:用并查集维护连通性,离线建出树(也不一定是树,也可能是森林),再用树链剖分维护修改和查询。
代码比较长,码风有点奇怪。

代码

CPP
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define V vector
#define FOR(i,a,b) for(int i=(int)(a);i<=(int)(b);i++)
#define pb push_back
const int INF=1e9+10,N=5e5+10;
struct SegTree{
	struct node{
		int l,r,w,tag;
	};
	V<node>a;
	SegTree(int _n):a(_n*4+2){}
	#define ls(x) (x<<1)
	#define rs(x) (x<<1|1)
	#define mid ((a[x].l+a[x].r)>>1)
	#define push_up(x) (a[(x)].w=a[ls((x))].w+a[rs((x))].w)
	void build(int x,int l,int r){
		a[x].l=l;a[x].r=r;a[x].tag=INF;
		if(l==r)return;
		build(ls(x),l,mid);
		build(rs(x),mid+1,r);
		push_up(x);
	}
	void addtag(int x,int w){
		a[x].tag=w;
		a[x].w=(a[x].r-a[x].l+1)*w;
	}
	void push_down(int x){
		if(a[x].tag!=INF){
			addtag(ls(x),a[x].tag);
			addtag(rs(x),a[x].tag);
			a[x].tag=INF;
		}
	}
	void update(int x,int L,int R,int w){
		if(a[x].l>=L&&a[x].r<=R){
			addtag(x,w);
			return;
		}
		push_down(x);
		if(L<=mid) update(ls(x),L,R,w);
		if(R>mid) update(rs(x),L,R,w);
		push_up(x);
	}
	int query(int x,int L,int R){
		if(a[x].l>=L&&a[x].r<=R) return a[x].w;
		push_down(x);
		if(R<=mid) return query(ls(x),L,R);
		else if(L>mid) return query(rs(x),L,R);
		else return query(ls(x),L,R)+query(rs(x),L,R);
	}
	#undef ls
	#undef rs
	#undef mid
	#undef push_up
};
struct DSU{
	V<int>fa;
	DSU(int _n):fa(_n+2){
		iota(fa.begin(),fa.end(),0);
	}
	int fin(int x){
		while(x^fa[x]) x=fa[x]=fa[fa[x]];
		return x;
	}
	bool is_same(int u,int v){
		return fin(u)==fin(v);
	}
	void merge(int u,int v){
		u=fin(u);v=fin(v);
		if(u==v) return;
		fa[u]=v;
	}
};
V<int>e[N];
int dep[N],siz[N],son[N],fa[N],top[N],id[N];
int num=0;
void dfs1(int u,int fat){
	fa[u]=fat;siz[u]=1;
	dep[u]=dep[fat]+1;
	for(auto v:e[u]){
		if(v==fat)continue;
		dfs1(v,u);
		siz[u]+=siz[v];
		if(!son[u]||siz[son[u]]<siz[v]) son[u]=v;
	}
}
void dfs2(int u,int tp){
	top[u]=tp;id[u]=++num;
	if(!son[u]) return;
	dfs2(son[u],tp);
	for(auto v:e[u]){
		if(v!=son[u]&&v!=fa[u]) dfs2(v,v);
	}
}
void update(SegTree &lls,int u,int v,int w){
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]]) swap(u,v);
		lls.update(1,id[top[u]],id[u],w);
		u=fa[top[u]];
	}
	if(dep[u]>dep[v]) swap(u,v);
	if(u!=v) lls.update(1,id[u]+1,id[v],w);
}
int query(SegTree &lls,int u,int v){
	int ans=0;
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]]) swap(u,v);
		ans+=lls.query(1,id[top[u]],id[u]);
		u=fa[top[u]];
	}
	if(dep[u]>dep[v]) swap(u,v);
	if(u!=v) ans+=lls.query(1,id[u]+1,id[v]);
	return ans;
}
struct QUE{
	int op,u,v,val,ans;
};
signed main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	int n,q;cin>>n>>q;
	V<QUE>que(q+1);
	SegTree lls(n+1);lls.build(1,1,n);
	DSU bcj(n+1); 
	FOR(i,1,q){
		int &op=que[i].op,&u=que[i].u,&v=que[i].v;
		cin>>op>>u>>v;
		if(op&1){
			if(!bcj.is_same(u,v)){
				bcj.merge(u,v);
				e[u].pb(v);e[v].pb(u);
				que[i].val=1;
			}
		}else{
			if(!bcj.is_same(u,v)){
				que[i].ans=-1;
			}
		}
	}
	FOR(i,1,n){
		if(!id[i]) dfs1(i,0),dfs2(i,i);
	}
	FOR(i,1,q){
		int op=que[i].op,u=que[i].u,v=que[i].v,ans=que[i].ans,val=que[i].val;
		if(op&1) update(lls,u,v,val);
		else{
			if(ans==-1) cout<<ans<<"\n";
			else cout<<query(lls,u,v)<<"\n";
		}
	}
	return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...