P3714 [BJOI2017] 樹的難題

小超手123發表於2024-03-19

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
*/

相關文章