LGP7880 [Ynoi 2006] rldcot 学习笔记
LGP7880 [Ynoi 2006] rldcot 学习笔记
前言
本题解主要参考:这篇。
题意简述
给定一棵 \(n\) 个结点的有根树。定义一个点的深度 \(\text{dep}_u\) 为 \(u\) 到根路径上的边权和。\(m\) 次询问,每次给定一个区间 \([l,r]\),问对于所有的 \(l\le i\le j\le r\),\(\text{dep}_{\text{lca}(i,j)}\) 组成的集合中数值的种数。
\(n\le 10^5,m\le 5\times 10^5,|d|\le 10^9\)。
做法解析
下文中,\(u\) 的“儿子树”指 \(u\) 的儿子 \(v\) 的子树。讨论的所有点对 \((i,j)\) 均满足 \(i\le j\)。
首先我们发现,这道题等价于一个 \(\text{2-side}\) 矩形数颜色问题。什么意思呢?我们考虑我们把所有 \((i,j)\) 以 \(i\) 为横坐标,\(j\) 为纵坐标摊在平面上,此时一个形如 \([l,r]\) 的询问实际上就是在问满足 \(x\ge l,y\le r\) 的 \((x,y)\) 数量。\(\text{2-side}\) 指的是所有询问的矩形有两条边没贴着边线,或者说所有询问的矩形都是一个二维的缀(在这题就是右下角矩形)。
这个东西我们怎么做呢?我们从右往左扫整个平面,维护每种颜色的点当前最低在哪个 \(y\) 坐标能取到,让树状数组在这个 \(y\) 处保持这个颜色的 \(1\) 贡献。对于一个询问 \([x,y]\) 就是 \(x\) 坐标到了直接查树状数组中 \([1,y]\) 即可。
但是所有点对的数量级是 \(n^2\) 的,令人谔谔。
然而,接下来我们将会证得,真正需要考虑的点对只有 \(n\log n\) 级别!
考虑一个情景:\(\exists\,a<c<d<b\) 满足 \(\text{lca}(a,b)=\text{lca}(c,d)=u\)。那么这个时候你发现 \((a,b)\) 这个点对就是没用的,因为一个包含 \((a,b)\) 的区间也一定包含 \((c,d)\),我只保留 \((c,d)\) 就足以让我统计到这份答案了。
这样,我们称 \((c,d)\) 支配了 \((a,b)\)。进一步我们定义一个点对是支配对当且仅当其不被支配。所有支配对构成的集合在此问题上等效于原来那个大小为 \(n^2\) 的集合。
我们得把这些支配对找出来,考虑其判定。经典地,我们在 \(\text{lca}\) 处统计。当一个点对 \((u,v)\) 满足 \(\text{lca}(u,v)=x\) 时,首先 \(u,v\) 要在 \(x\) 的不同儿子树。然后,记 \(T\) 为 \(x\) 的儿子树中 \(u\) 所在儿子树外的点构成的集合,则 \(v\) 必须是 \(T\) 中 \(u\) 在结点编号上的前驱或后继(当然,还有一种情况是 \(u=v=x\))。
这个东西实则是个树上启发式合并的形式。说到这各位想必豁然开朗了:树上启发式合并来了,时间复杂度就有救了。
根据树上启发式合并的性质,一个点只会被遍历 \(\log n\) 次,每次遍历的时候会找前驱和后继,增加的点对是 \(O(1)\) 的。所以最后我们得到的“支配对”集合大小就是 \(O(n\log n)\) 的,非常可以接受!不过实际上,“一个点对被选进这个集合”是“这个点对是支配对”的必要不充分条件,因为我们在更新过程中是一个一个加儿子的子树的,可能存在一个点对在考虑前若干儿子树时是支配对,但是被后来的儿子树里的某个点支配。
现在二维矩形数颜色复杂度就降到 \(O(n\log^2n)\) 了!我们这个题就做完了。
代码实现
#include <bits/stdc++.h>
using namespace std;
using namespace obasic;
const int MaxN=1e5+5;
int N,M,X,Y,Z;struct edge{int v,w;};vector<edge> Tr[MaxN];
void addudge(int u,int v,int w){
Tr[u].push_back({v,w});
Tr[v].push_back({u,w});
}
int siz[MaxN],hson[MaxN],tfa[MaxN];lolo dep[MaxN];
void dfs1(int u,int f){
siz[u]=1,tfa[u]=f;
for(auto [v,w] : Tr[u]){
if(v==f)continue;dep[v]=dep[u]+w,dfs1(v,u);
siz[u]+=siz[v];if(siz[v]>siz[hson[u]])hson[u]=v;
}
}
lolo D[MaxN];int C[MaxN],nln,ans[MaxN];
struct anob{int a,b;};int pos[MaxN];
set<int> S;vector<anob> U[MaxN],Q[MaxN];
int gpre(int x){auto ite=S.lower_bound(x);return ite==S.begin()?0:*(--ite);}
int gnxt(int x){auto ite=S.upper_bound(x);return ite==S.end()?0:*ite;}
void afind(int u,int c){
int t;
t=gpre(u);if(t)U[t].push_back({u,c});
t=gnxt(u);if(t)U[u].push_back({t,c});
}
void calc(int u,int c){afind(u,c);for(auto [v,w] : Tr[u])if(v!=tfa[u])calc(v,c);}
void radd(int u){S.insert(u);for(auto [v,w] : Tr[u])if(v!=tfa[u])radd(v);}
void dfs2(int u,int op){
for(auto [v,w] : Tr[u])if(v!=tfa[u]&&v!=hson[u])dfs2(v,1);
if(hson[u]){dfs2(hson[u],0);};S.insert(u);
for(auto [v,w] : Tr[u])if(v!=tfa[u]&&v!=hson[u])calc(v,C[u]),radd(v);
if(op)S.clear();
}
struct BinidTree{
int n,t[MaxN];
void init(int x){n=x,fill(t,t+n+1,0);}
int lowbit(int x){return x&(-x);}
void add(int p,int x){for(;p<=n;p+=lowbit(p))t[p]+=x;}
int gts(int p){int res=0;for(;p;res+=t[p],p-=lowbit(p));return res;}
}BiT;
int main(){
readis(N,M),BiT.init(N);
for(int i=1;i<N;i++)readis(X,Y,Z),addudge(X,Y,Z);
dfs1(1,0);for(int i=1;i<=N;i++)D[i]=dep[i];
sort(D+1,D+N+1),nln=unique(D+1,D+N+1)-(D+1);
for(int i=1;i<=N;i++)C[i]=lwberi(D,nln,dep[i]);
dfs2(1,0);for(int i=1;i<=nln;i++)pos[i]=N+1;
for(int i=1;i<=N;i++)U[i].push_back({i,C[i]});
for(int i=1;i<=M;i++)readis(X,Y),Q[X].push_back({Y,i});
for(int i=N;i>=1;i--){
for(auto [x,c] : U[i])BiT.add(pos[c],-1),minner(pos[c],x),BiT.add(pos[c],1);
for(auto [x,id] : Q[i])ans[id]=BiT.gts(x);
}
for(int i=1;i<=M;i++)writil(ans[i]);
return 0;
}
后记
树上启发式合并得了MVP!矩形数颜色就是躺赢狗!后面忘了。
浙公网安备 33010602011771号