当前位置: 代码迷 >> 综合 >> P3899-[湖南集训]谈笑风生【主席树】
  详细解决方案

P3899-[湖南集训]谈笑风生【主席树】

热度:75   发布时间:2024-02-22 12:24:01.0

正题

题目链接:https://www.luogu.com.cn/problem/P3899


题目大意

给出nnn个点的一棵有根树,每次询问一个(p,k)(p,k)(p,k),求有多少个点对(b,c)(b,c)(b,c)满足

  1. pppbbbccc的祖先
  2. bbbppp的距离不超过kkk
    蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤\color{white}蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤

解题思路

首先如果bbbaaa上方,那么点bbb的个数可以用深度来求,而ccc的数量就是aaa的子树大小?1-1?1

如果bbbaaa的下方,ccc的数量就是bbb的子树大小?1-1?1,也就是对于每个bbb它的贡献是它的子树大小?1-1?1,那么就求我们在aaa的子树中与aaa距离不超过kkk的点的权值和即可。

这个可以用dfsdfsdfs序和主席树维护,时间复杂度O(nlog?n)O(n\log n)O(nlogn)


codecodecode

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define siz(x) (ed[x]-dfn[x]+1)
using namespace std;
const ll N=3e5+10,M=6e6+10;
struct node{
    ll to,next;
}a[N*2];
ll n,q,tot,cnt,D,ls[N],dep[N];
ll dfn[N],ed[N],rt[N],rfn[N];
void addl(ll x,ll y){
    a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;
}
void dfs(ll x,ll fa){
    dfn[x]=++cnt;rfn[cnt]=x;dep[x]=dep[fa]+1;D=max(D,dep[x]);for(ll i=ls[x];i;i=a[i].next){
    ll y=a[i].to;if(y==fa)continue;dfs(y,x);}ed[x]=cnt;
}
struct Seg_Tree{
    ll cnt,sum[M],ls[M],rs[M];ll Change(ll x,ll L,ll R,ll pos,ll val){
    ll y=++cnt;sum[y]=sum[x]+val;if(L==R){
    return y;}ll mid=(L+R)>>1;if(pos<=mid)ls[y]=Change(ls[x],L,mid,pos,val),rs[y]=rs[x];else rs[y]=Change(rs[x],mid+1,R,pos,val),ls[y]=ls[x];return y;}ll Ask(ll x,ll y,ll L,ll R,ll l,ll r){
    if(!(sum[y]-sum[x]))return 0;if(L==l&&R==r)return sum[y]-sum[x];ll mid=(L+R)>>1;if(r<=mid)return Ask(ls[x],ls[y],L,mid,l,r);if(l>mid)return Ask(rs[x],rs[y],mid+1,R,l,r);return Ask(ls[x],ls[y],L,mid,l,mid)+Ask(rs[x],rs[y],mid+1,R,mid+1,r);}
}T;
int main()
{
    scanf("%lld%lld",&n,&q);for(ll i=1;i<n;i++){
    ll x,y;scanf("%lld%lld",&x,&y);addl(x,y);addl(y,x);}dfs(1,0);for(ll i=1;i<=n;i++){
    int x=rfn[i];rt[i]=T.Change(rt[i-1],1,D,dep[x],siz(x)-1);}while(q--){
    ll p,k;scanf("%lld%lld",&p,&k);ll ans=min(k,dep[p]-1)*(siz(p)-1);printf("%lld\n",ans+T.Ask(rt[dfn[p]],rt[ed[p]],1,D,dep[p]+1,dep[p]+k));}
}