P13921 [PO Final 2024] 回家 Trafikverket's Mistake / 重剖 + 线段树优化建图

题目传送门:P13921 [PO Final 2024] 回家 Trafikverket's Mistake

设题目说的 \(W_i,H_i\)\(l_i,r_i\)

首先有一个最暴力的算法,若 \(l_i,r_i\) 的路径没有遮挡,直接跑,否则继续等待,我们发现这样非常类似拓扑排序,直接一开始对满足在 \(l_i,r_i\) 的路径上的 \(l_j\),连 \(j\)\(i\) 的有向边,表示 \(j\) 要先跑,\(i\) 才能跑,直接拓扑排序即可。

但是这样做是 \(O(n^2)\) 的,需要考虑优化。

考虑对原树重剖,对每条重链都建一颗线段树,那么 \(l_i\)\(r_i\) 的路径最多只会经过 \(O(\log n)\) 条重链,然后直接边跳重链边线段树优化建图即可。

这里我们可以将每个 \(l_i\) 出现在线段树的位置标记一下,然后拓扑排序时只需要存标记过的位置,注意建图时要小心连成自环而且 \(l_i\) 不能被连因为自己不会堵自己,具体的可以看代码。

#include<bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
const int N=2e5+10; 
inline int read(){
	char c=getchar();
	int f=1,ans=0;
	while(c<48||c>57) f=(c==45?f=-1:1),c=getchar();
	while(c>=48&&c<=57) ans=(ans<<1)+(ans<<3)+(c^48),c=getchar();
	return ans*f;
}
int n,m,sz[N],son[N],top[N],bot[N],dep[N],fa[N],nw[N],dfn[N],cnt;
vector<int>g1[N],g2[N<<2];
void dfs1(int u,int fa){
	sz[u]=1,dep[u]=dep[fa]+1,::fa[u]=fa;
	for (auto v:g1[u]) if (v^fa){
		dfs1(v,u);
		sz[u]+=sz[v];
		if (sz[son[u]]<sz[v]) son[u]=v;
	}
} 
void dfs2(int u,int t){
	dfn[u]=++cnt,nw[cnt]=u,top[u]=t,bot[t]=u;
	if (!son[u]) return ;
	dfs2(son[u],t);
	for (auto v:g1[u]) if (v!=fa[u]&&v!=son[u]) dfs2(v,v);
}
int root[N],ls[N<<2],rs[N<<2],tot,id[N],d[N<<2],vis[N<<2];
#define lc ls[k]
#define rc rs[k]
void build(int &k,int l,int r){
	if (!k) k=++tot;
	if (l==r){id[nw[l]]=k;return ;}
	int mid=l+r>>1;
	build(lc,l,mid),build(rc,mid+1,r);
	g2[lc].push_back(k),g2[rc].push_back(k),d[k]+=2;
}
void change(int k,int l,int r,int l1,int r1,int x){
	if (l1>r1) return ;
	if (l1<=l&&r1>=r){if (k!=x) g2[k].push_back(x),d[x]++;return ;}
	int mid=l+r>>1;
	if (l1<=mid) change(lc,l,mid,l1,r1,x);
	if (r1>mid) change(rc,mid+1,r,l1,r1,x);
}
inline void topsort(){
	queue<int>q;
	for (int i=1;i<=tot;i++) if (!d[i]) q.push(i);
	vector<int>anss;
	while(!q.empty()){
		int u=q.front();q.pop();
		if (vis[u]) anss.push_back(vis[u]);
		for (auto v:g2[u]) if (--d[v]==0) q.push(v);
	}
	if ((int)anss.size()!=m) puts("No");
	else{
		puts("Yes");
		for (auto i:anss) printf("%lld ",i);
	}
}
int l[N],r[N],mp[N];
inline void solve(int u,int v){
	if (u==v) return ;
	int tmp=u;
	while(top[u]^top[v]){
		if (dep[top[u]]<dep[top[v]]) swap(u,v);
		int l=dfn[top[u]],r=dfn[u];
		if (l==dfn[tmp]) l++;
		if (r==dfn[tmp]) r--;
		change(root[top[u]],dfn[top[u]],dfn[bot[top[u]]],l,r,id[tmp]); 
		u=fa[top[u]];
	}
	if (dep[u]<dep[v]) swap(u,v);
	int l=dfn[v],r=dfn[u];
	if (l==dfn[tmp]) l++;
	if (r==dfn[tmp]) r--;
	change(root[top[u]],dfn[top[u]],dfn[bot[top[u]]],l,r,id[tmp]); 
} 
main(){
	n=read(),m=read();
	for (int i=1;i<n;i++){
		int u=read(),v=read();
		g1[u].push_back(v),g1[v].push_back(u); 
	}
	dfs1(1,0),dfs2(1,1);
	for (int i=1;i<=n;i++) if (!mp[top[i]]) build(root[top[i]],dfn[top[i]],dfn[bot[top[i]]]),mp[top[i]]=1;
	for (int i=1;i<=m;i++) l[i]=read(),r[i]=read(),vis[id[l[i]]]=i,solve(l[i],r[i]);
	topsort();
    return 0;
}
posted @ 2026-01-15 11:02  OTn53_qwq  阅读(6)  评论(0)    收藏  举报