*题解:P7880 [Ynoi2006] rldcot

原题链接

解析

考虑每个 lca 对应的点对,对于一个点 \(x\)\(\operatorname{lca}(u,v)=x\) 当且仅当 \(u\)\(v\) 来自于 \(x\) 的不同子树或者 \(u,v\) 中至少有一个是 \(x\)。 然而这 \(O(n^2)\) 个点对并不是都有用,对于点对 \((u,v),(x,y)\) 满足 \(\operatorname{lca}(u,v)=\operatorname{lca}(x,y),u \le x \le y \le v\),点对 \((u,v)\) 是没有用的,因为如果询问覆盖了 \([u,v]\) 就必定覆盖了 \([x,y]\),只保留 \((x,y)\) 不影响结果。于是就有一种想法,在 \(x\) 的子树统计这样的极短区间 \([u,v]\) 使得 \(\operatorname{lca}(u,v) = x\),统计方式就是对于一个点 \(u\),找 \(u\) 在其他子树中的前驱和后继。这个过程可以使用启发式合并 + set 来实现,重儿子所在子树信息保留,对于其余子树,遍历的同时尝试在 set 里找前驱后继,结束后再把子树内的点加入 set。根据启发式合并的复杂度,可以得知处理出的区间数量是 \(O(n\log n)\) 的。

现在问题变为给定若干个带有颜色的区间,再给定询问区间,求它所包含的颜色区间中有多少种颜色。考虑扫描线,从右往左扫,维护 \(pos_i\) 表示当前在扫描线右边的区间中颜色为 \(i\) 的右端点位置最小值。查询 \([l,r]\) 就变为查询有多少个 \(i\) 满足 \(pos_i\le r\),可以使用树状数组进行前缀查询。

时间复杂度 \(O(n\log^2n)\)

代码

#include <bits/stdc++.h>
#define ls(p) ((p) << 1)
#define rs(p) (((p) << 1) | 1)
#define mid ((l + r) >> 1)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N = 1e5 + 5,M = 5e5,mod = 998244353;
vector<pii> t[N];
ll dep[N];
int siz[N],son[N],cnt[N],pos[N];
int b[N];
int res[M];
int read(){
	int a = 1,x = 0;
	char ch = getchar();
	while(ch > '9' || ch < '0'){
		if(ch == '-') a = -1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9'){
		x = x * 10 + ch - '0';
		ch = getchar();
	}
	return a * x;
}
void modi(int x,int k){
	for(;x < N;x += x & -x){
		b[x] += k;
	}
}
int ask(int x){
	int res = 0;
	for(;x;x -= x & -x){
		res += b[x];
	}
	return res;
}
struct Seg{
	int l,r;
	ll d;
	friend bool operator < (Seg a,Seg b){
		return a.l < b.l;
	}
};
struct Query{
	int l,r,id;
	friend bool operator < (Query a,Query b){
		return a.l < b.l;
	}
};
vector<Seg> sg;
void dfs(int x,int f,ll d){
	siz[x] = 1;
	dep[x] = d;
	for(pii p : t[x])if(p.first != f){
		int nx = p.first,w = p.second;
		dfs(nx,x,d + w);
		siz[x] += siz[nx];
		if(siz[nx] > siz[son[x]]) son[x] = nx;
	}
}
set<int> s;
void calc(int x,int f,ll d){
	auto it = s.lower_bound(x);
	if(it != s.end()) sg.push_back({x,*it,d});
	if(it != s.begin()) sg.push_back({*prev(it),x,d});
	for(pii p : t[x])if(p.first != f){
		int nx = p.first;
		calc(nx,x,d);
	}
}
void add(int x,int f){
	for(pii p : t[x])if(p.first != f){
		int nx = p.first;
		add(nx,x);
	}
	s.insert(x);
}
void clear(){
	s.clear();
}
void sol(int x,int f){
	for(pii p : t[x])if(p.first != f && p.first != son[x]){
		int nx = p.first;
		sol(nx,x);
		clear();
	}
	if(son[x]){
		sol(son[x],x);
	}
	 
	for(pii p : t[x])if(p.first != f && p.first != son[x]){
		int nx = p.first;
		calc(nx,x,dep[x]);
		add(nx,x);
	}
	s.insert(x);
	auto it = s.lower_bound(x);
	if(it != s.end()) sg.push_back({x,*it,dep[x]});
	if(it != s.begin()) sg.push_back({*prev(it),x,dep[x]});
	
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	memset(pos,127,sizeof(pos));
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	int n = read(),m = read();
	for(int i=1;i<n;i++){
		int u = read(),v = read(),d = read();
		t[u].push_back({v,d});
		t[v].push_back({u,d});
	}
	dfs(1,0,0);
	sol(1,0);
	vector<ll> v;
	for(int i=1;i<=n;i++){
		v.push_back(dep[i]);
	}	
	sort(v.begin(),v.end());
	v.erase(unique(v.begin(),v.end()),v.end());
	for(int i=0;i<sg.size();i++){
		sg[i].d = lower_bound(v.begin(),v.end(),sg[i].d) - v.begin() + 1;
	}
	sort(sg.begin(),sg.end());
	vector<Query> q;
	for(int i=1;i<=m;i++){
		int l = read(),r = read();
		q.push_back({l,r,i});
	}
	sort(q.begin(),q.end());
	int p1 = q.size() - 1,p2 = sg.size() - 1;
	for(int i=n;i>=1;i--){
		while(p2 >= 0 && sg[p2].l == i){
			if(sg[p2].r < pos[sg[p2].d]){
				if(pos[sg[p2].d] < 2e9){
					modi(pos[sg[p2].d],-1); 
				}	
				pos[sg[p2].d] = sg[p2].r;
				modi(sg[p2].r,1); 
			}
			p2--;
		}
		
		while(p1 >= 0 && q[p1].l == i){
			res[q[p1].id] = ask(q[p1].r);
			p1--;
		}
	}
	for(int i=1;i<=m;i++){
		cout<<res[i]<<'\n';
	}
	return 0;
}
posted @ 2025-11-14 17:44  yutar  阅读(7)  评论(0)    收藏  举报