P3714 [BJOI2017] 树的难题

from xcs

题意:

给定一棵 \(n\) 个节点的树,树根为 \(1\),每个点有一个编号,每条边有一个边权。

定义 \(dep(x)\) 表示一个点到根简单路径上边权的和,\(lca(x,y)\) 表示 \(x,y\) 节点在树上的最近公共祖先。

\(m\) 组询问,每次询问给出 \(l,r\),求对于所有点编号的二元组 \((i,j)\) 满足 \(l \le i,j \le r\) ,有多少种不同的 \(dep( lca(i,j))\)

\(1\le n \le 10^5\)\(1\le m \le 5 \times 10^5\),所有数值为整数,\(-10^9 \le d \le 10^9\)

分析:

显然需要在 LCA 处(记作 \(x\))统计答案,对于 \(u,v\),它们的 LCA 为 \(x\) 当且仅当,\(u,v\)\(x\) 不同的子树内。暴力遍历两个不同的子树有效点对仍是 \(n^2\) 的。

考虑去掉一些一定不优的点对。假设存在两个点对 \((u_{1},v_{1})\)\((u_{2},v_{2})\) 满足 \(u_{1} \le u_{2} \le v_{2} \le v_{1}\),那么 \((u_{1},v_{2})\) 一定不优。

使用 dsu on tree,先算出重儿子的 set,遍历轻儿子的时候在 set 里找一个前驱后继即可,跑完一个子树,就把这个子树加入到 set 里。由这个做法可以推断出有效的点对的数量级是 \(n \log n\) 的。

现在问题变成了给定若干个区间 \([l, r] \ :w\),然后询问 \([l,r]\) 里包含的所有区间的 \(w\) 的不同个数。

直接扫描线即可。时间复杂度 \(O(n \log n^2+q)\)

代码:

#include<bits/stdc++.h>
//#define int long long
#define N 500005
#define il inline
using namespace std;
namespace IO{
    char buff[1<<21],*p1=buff,*p2=buff;
    char getch(){
        return p1==p2&&(p2=((p1=buff)+fread(buff,1,1<<21,stdin)),p1==p2)?EOF:*p1++;
    }
    template<typename T>
    void read(T &x){
        char ch=getch();int fl=1;x=0;
        while(ch>'9'||ch<'0'){if(ch=='-')fl=-1;ch=getch();}
        while(ch<='9'&&ch>='0'){x=x*10+ch-48;ch=getch();}
        x*=fl;
    }
    template<typename T,typename ...Args>
    void read(T &x,Args& ...args){
        read(x);read(args...);
    }
    char obuf[1<<21],*p3=obuf;
    void putch(char ch){
        if(p3-obuf<(1<<21))*p3++=ch;
        else fwrite(obuf,p3-obuf,1,stdout),p3=obuf,*p3++=ch;
    }
    char ch[100];
    template<typename T>
    void write(T x){
        if(!x)return putch('0');
        if(x<0)putch('-'),x*=-1;
        int top=0;
        while(x)ch[++top]=x%10+48,x/=10;
        while(top)putch(ch[top]),top--;
    }
    template<typename T,typename ...Args>
    void write(T x,Args ...args){
        write(x);write(args...);
    }
    void flush(){fwrite(obuf,p3-obuf,1,stdout);}
}
using namespace IO;
int n, m, cnt;
long long dis[N];
int siz[N], son[N];
struct edge {
	int to, w;
};
struct node {
	int r, w;
};
vector<node>G[N];
vector<edge>p[N];
set<int>s;
void dfs(int x, int fa) {
	int Maxson = -1;
	siz[x] = 1;
	int Len = p[x].size();
	for(int i = 0; i < Len; i++) {
		int y = p[x][i].to;
		if(y == fa) continue;
		dis[y] = dis[x] + p[x][i].w;
		dfs(y, x);
		siz[x] += siz[y];
		if(siz[y] > Maxson) {
			Maxson = siz[y];
			son[x] = y;
		}
	}
}
int h[N], tot;
void Sol(int x, int fa, int W) {
	h[++tot] = x;
	auto L = s.lower_bound(x);
	auto R = L;
	if(L != s.begin()) {
		L--;
		int l = (*L);
		if(l <= x) G[l].push_back((node){x, W});
	}
	if(R != s.end()) {
		int r = (*R);
		if(x <= r) G[x].push_back((node){r, W});
	}
	int Len = p[x].size();
	for(int i = 0; i < Len; i++) {
		int y = p[x][i].to;
		if(y == fa) continue;
		Sol(y, x, W);
	}
}
void work(int x, int fa, bool opt) { //optè¡¨ç¤ºæ˜¯å¦åˆ é™¤
	int Len = p[x].size();
	for(int i = 0; i < Len; i++) {
		int y = p[x][i].to;
		if(y == son[x] || y == fa) continue;
		work(y, x, 1);
	}
	
	if(son[x]) work(son[x], x, 0);
	G[x].push_back((node){x, (int)dis[x]});
	s.insert(x);
	for(int i = 0; i < Len; i++) {
		int y = p[x][i].to;
		if(y == son[x] || y == fa) continue;
		tot = 0;
		Sol(y, x, dis[x]);
		for(int j = 1; j <= tot; j++) s.insert(h[j]);
	}
	if(opt) s.clear();
}
int c[N];
il void add(int x, int y) {
	for(int i = x; i <= n; i += (i & (-i))) c[i] += y;
}
il int query(int x) {
	int res = 0;
	for(int i = x; i; i -= (i & (-i))) res += c[i];
	return res;
}
struct ask {
	int r, id;
};
vector<ask>z[N];
long long b[N];
int Min[N], ans[N];
il void Hash() {
	for(int i = 1; i <= n; i++) b[i] = dis[i], Min[i] = n + 1;
	sort(b + 1, b + n + 1);
	int len = unique(b + 1, b + n + 1) - b - 1;
	for(int i = 1; i <= n; i++) dis[i] = lower_bound(b + 1, b + len + 1, dis[i]) - b;
}
signed main() {
	//freopen("tree.in", "r", stdin);
	//freopen("tree.out", "w", stdout);
	read(n, m);
	for(int i = 1, u, v, w; i < n; i++) {
		read(u, v, w);
		p[u].push_back((edge){v, w});
		p[v].push_back((edge){u, w});
	}
	dfs(1, 0);
	Hash();
	work(1, 0, 0);
	for(int i = 1, l, r; i <= m; i++) {
		read(l, r); 
		z[l].push_back((ask){r, i});
	}
	for(int l = n; l >= 1; l--) {
		for(int j = 0; j < G[l].size(); j++) {
			if(G[l][j].r <= Min[G[l][j].w] - 1) {
				add(G[l][j].r, 1);
				add(Min[G[l][j].w], -1);
				Min[G[l][j].w] = G[l][j].r;
			}
		}
		for(int j = 0; j < z[l].size(); j++) 
			ans[z[l][j].id] = query(z[l][j].r);
	}
	for(int i = 1; i <= m; i++) {
		write(ans[i]);
		putch('\n');
	}
	flush();
	return 0;
}
/*
5 1
1 2 2
1 3 2
3 4 3
1 5 1
1 3
*/
posted @ 2024-03-19 21:52  小超手123  阅读(15)  评论(0编辑  收藏  举报