LGP3899 [HN CtCamp] 谈笑风生 学习笔记
LGP3899 [HN CtCamp] 谈笑风生 学习笔记
前言
致敬传奇土著神洩矢诹访子,致敬传奇鸦天狗射命丸文。
题意简述
对于一个有根树的两个结点,设其中有两个不同结点 \(a,b\)。有如下定义:
- 如果 \(a\) 是 \(b\) 的祖先,那么称“\(a\) 比 \(b\) 不知道高到哪去了”。
- 如果 \(a\) 和 \(b\) 的距离不超过某个给定常数 \(x\),那么称“\(a\) 和 \(b\) 谈笑风生”。
给定一棵 \(n\) 个结点,以 \(1\) 为根的树。回答 \(m\) 个询问,每个询问给定两个常数 \(a,k\),问有多少个二元组 \((b,c)\) 满足:
- \(a,b,c\) 两两不同。
- \(a,b\) 都比 \(c\) 不知道高到哪去了。
- \(a\) 和 \(b\) 谈笑风生。
做法解析
这题可以离线!我猜我们转化问题后可以直接用树状数组干掉这个二维数点。事实也的确如此。
一个关键的观察是,“\(a,b\) 都比 \(c\) 不知道高到哪去了”说明 \(a,b,c\) 处在同一条直链(深度单调变化的链)上,说明 \(a,b\) 中一个点一定是另一个的祖先。这样我们极大地锁定了要数的点的范围。
接下来分类讨论(设根结点深度为 \(1\))。
如果 \(b\) 是 \(a\) 的祖先,此时 \(c\) 可以在 \(a\) 的子树里面随便选(\(a\) 自身除外),二元组数量就是 \(\min(dep_a-1,k)\times (siz_a-1)\)。
如果 \(a\) 是 \(b\) 的祖先,二元组数量即为 \(\sum_{b\in \text{subtree}(a)}(siz_b-1)[dep_b\in [dep_a+1,dep_a+k]]\)。
而 \(b\in\text{subtree}(a)\) 又可以写成 \(dfn_b\in(dfn_a,dfn_a+siz_a)\)。
这不就是二维数点吗?离线树状数组秒了。
复杂度 \(O(n\log^2 n)\)。
代码实现
#include <bits/stdc++.h>
using namespace std;
const int MaxN=3e5+5;
int N,M,X,Y;lolo ans[MaxN];
vector<int> Tr[MaxN];
void addudge(int u,int v){
Tr[u].push_back(v);
Tr[v].push_back(u);
}
int dep[MaxN],dfn[MaxN],siz[MaxN],dcnt;
int fdep(int u,int d){return min(dep[u]+d,N);}
void dfs(int u,int f){
dep[u]=dep[f]+1,dfn[u]=++dcnt,siz[u]=1;
for(int v : Tr[u]){
if(v==f)continue;
dfs(v,u),siz[u]+=siz[v];
}
}
struct anob{int y,c;};vector<anob> A[MaxN];
struct quer{int ly,ry,id,k;};vector<quer> Q[MaxN];
struct BinidTree{
int n;lolo 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,lolo x){for(;p<=n;p+=lowbit(p))t[p]+=x;}
lolo gts(int p){lolo res=0;for(;p;res+=t[p],p-=lowbit(p));return res;}
lolo getsum(int l,int r){return gts(r)-gts(l-1);}
}BiT;
int main(){
readis(N,M),BiT.init(N);
for(int i=1;i<N;i++)readis(X,Y),addudge(X,Y);
dfs(1,0);for(int i=1;i<=N;i++)A[dfn[i]].push_back({dep[i],siz[i]-1});
for(int i=1;i<=M;i++){
readis(X,Y);
ans[i]+=1ll*min(dep[X]-1,Y)*(siz[X]-1);
Q[dfn[X]+siz[X]-1].push_back({fdep(X,1),fdep(X,Y),i,1});
Q[dfn[X]].push_back({fdep(X,1),fdep(X,Y),i,-1});
}
for(int i=1;i<=N;i++){
for(auto [y,c] : A[i])BiT.add(y,c);
for(auto [ly,ry,id,k] : Q[i])ans[id]+=k*BiT.getsum(ly,ry);
}
for(int i=1;i<=M;i++)writil(ans[i]);
return 0;
}
浙公网安备 33010602011771号