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
*/
标签:难题,le,int,siz,void,BJOI2017,P3714,ch,long
From: https://www.cnblogs.com/xcs123/p/18084061