LGP3899 [HN CtCamp] 谈笑风生 学习笔记

LGP3899 [HN CtCamp] 谈笑风生 学习笔记

Luogu Link

前言

致敬传奇土著神洩矢诹访子,致敬传奇鸦天狗射命丸文。

题意简述

对于一个有根树的两个结点,设其中有两个不同结点 \(a,b\)。有如下定义:

  • 如果 \(a\)\(b\) 的祖先,那么称“\(a\)\(b\) 不知道高到哪去了”。
  • 如果 \(a\)\(b\) 的距离不超过某个给定常数 \(x\),那么称“\(a\)\(b\) 谈笑风生”。

给定一棵 \(n\) 个结点,以 \(1\) 为根的树。回答 \(m\) 个询问,每个询问给定两个常数 \(a,k\),问有多少个二元组 \((b,c)\) 满足:

  1. \(a,b,c\) 两两不同。
  2. \(a,b\) 都比 \(c\) 不知道高到哪去了。
  3. \(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;
}
posted @ 2025-05-03 20:46  矞龙OrinLoong  阅读(13)  评论(0)    收藏  举报