题面

传送门:https://www.luogu.org/problemnew/show/P3899


Solution

你们搞的这道题啊,excited!

这题真的很有意思。

首先,我们可以先理解一下题面:固定一个a,找到一个b,c就是a与b的公共子树中的某个点

那么,我们显然可以把这个b分成两类,第一种是在a上面的,第二种在a下面的

对于b在a上面的情况,显然,c一定是a的子树中的某个点,答案即为min(K,depth[a])*size[a]

对于b在a下面的情况,问题就会变得比较exciting了。

我们可以考虑使用主席树来搞这个问题。

考虑建一颗这样的主席树,以节点深度为主席树下标

对于一个节点,如果B在这个节点上,那么,它对答案的贡献显然是它的size-1。

那么,我们把它的贡献插入到它对应的主席树中(以深度为下标)。

每一个子节点开一颗主席树,记录到它为止所有的深度的答案和,有点类似前缀和,以dfs序为时间轴

这样,我们可以用一种类似树上差分的办法来“抠”出一颗子树对应的线段数(以深度为下标),这颗线段数中的[depth[x]+1,depth[x]+K]区间的sum就是这个x点的答案啦

就酱,这题就被我们切掉啦٩(๑>◡<๑)۶


Code

#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
long long read()
{
long long x=0,f=1; char c=getchar();
while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
while(isdigit(c)){x=x*10+c-'0';c=getchar();}
return x*f;
}
const long long N=300000+100;
const long long M=N*30;
struct SegmentTree
{
#define mid ((now_l+now_r)>>1)
long long son[M][2],cnt;
long long sum[M];
inline void update(long long now)
{
sum[now]=sum[son[now][0]]+sum[son[now][1]];
}
void Build(long long now,long long now_l,long long now_r)
{
if(now_l==now_r)
return;
Build(son[now][0]=++cnt,now_l,mid);
Build(son[now][1]=++cnt,mid+1,now_r);
}
void Add(long long x,long long num,long long now,long long pre,long long now_l,long long now_r)
{
if(now_l==now_r)
{
sum[now]=sum[pre];
sum[now]+=num;
return ;
}
if(x<=mid) son[now][1]=son[pre][1],Add(x,num,son[now][0]=++cnt,son[pre][0],now_l,mid);
else son[now][0]=son[pre][0],Add(x,num,son[now][1]=++cnt,son[pre][1],mid+1,now_r);
update(now);
}
long long Query(long long L,long long R,long long now,long long pre,long long now_l,long long now_r)
{
if(now_l>=L and now_r<=R)
return sum[now]-sum[pre];
long long ans=0;
if(L<=mid) ans+=Query(L,R,son[now][0],son[pre][0],now_l,mid);
if(R>mid) ans+=Query(L,R,son[now][1],son[pre][1],mid+1,now_r);
return ans;
}
void Print(long long now,long long now_l,long long now_r)
{
cerr<<"no."<<now<<" now_l&r:"<<now_l<<" "<<now_r<<" sonl&r"<<son[now][0]<<" "<<son[now][1]<<" sum:"<<sum[now]<<endl;
if(now_l!=now_r)
{
Print(son[now][0],now_l,mid);
Print(son[now][1],mid+1,now_r);
}
}
#undef mid
}sgt;
vector <long long> e[N];
long long n,m,depth[N],size[N],dfn[N],dfn_to,r[N];
void dfs2(long long now)
{
dfn[now]=++dfn_to;
size[now]=1;
for(long long i=0;i<(long long)(e[now].size());i++)
if(depth[e[now][i]]==0)
{
depth[e[now][i]]=depth[now]+1;
dfs2(e[now][i]);
size[now]+=size[e[now][i]];
}
}
void dfs(long long now)
{
r[dfn[now]]=++sgt.cnt;
sgt.Add(depth[now],size[now]-1,r[dfn[now]],r[dfn[now]-1],1,n);
//sgt.Print(r[dfn[now]],1,n);
//cerr<<endl;
for(long long i=0;i<(long long)(e[now].size());i++)
if(depth[e[now][i]]>depth[now])
dfs(e[now][i]);
}
int main()
{
n=read(),m=read();
for(long long i=1;i<=n;i++)
e[i].reserve(4);
for(long long i=1;i<n;i++)
{
long long s=read(),t=read();
e[s].push_back(t);
e[t].push_back(s);
} sgt.Build(0,1,n);
//sgt.Print(0,1,n);
//cerr<<endl;
depth[1]=1;
dfs2(1);
dfs(1); for(long long i=1;i<=m;i++)
{
long long p=read(),K=read();
long long ans=sgt.Query(depth[p]+1,depth[p]+K,r[dfn[p]+size[p]-1],r[dfn[p]-1],1,n);
ans+=min(K,depth[p]-1)*(size[p]-1);
printf("%lld\n",ans);
}
return 0;
}

[Luogu P3899] [湖南集训]谈笑风生 (主席树)的更多相关文章

  1. P3899 [湖南集训]谈笑风生 主席树

    #include<iostream> #include<string.h> #include<algorithm> #include<stdio.h> ...

  2. luogu P3899 [湖南集训]谈笑风生 线段树合并

    Code: #include<bits/stdc++.h> #define maxn 300002 #define ll long long using namespace std; vo ...

  3. 主席树 || 可持久化线段树 || BZOJ 3653: 谈笑风生 || Luogu P3899 [湖南集训]谈笑风生

    题面:P3899 [湖南集训]谈笑风生 题解: 我很喜欢这道题. 因为A是给定的,所以实质是求二元组的个数.我们以A(即给定的P)作为基点寻找答案,那么情况分两类.一种是B为A的父亲,另一种是A为B的 ...

  4. luogu P3899 [湖南集训]谈笑风生

    传送门 nmyzd,mgdhls,bnmbzdgdnlql,a,wgttxfs 对于一个点\(a\),点\(b\)只有可能是他的祖先或者在\(a\)子树里 如果点\(b\)是\(a\)祖先,那么答案为 ...

  5. 洛谷P3899 [湖南集训]谈笑风生(线段树合并)

    题意 题目链接 Sol 线段树合并板子题,目前我看到两种写法,分别是这样的. 前一种每次需要新建一个节点,空间是\(O(4nlogn)\) 后者不需要新建,空间是\(O(nlogn)\)(面向数据算空 ...

  6. Luogu 3899 [湖南集训]谈笑风生

    BZOJ 3653权限题. 这题方法很多,但我会的不多…… 给定了$a$,我们考虑讨论$b$的位置: 1.$b$在$a$到根的链上,那么这样子$a$的子树中的每一个结点(除了$a$之外)都是可以成为$ ...

  7. P3899 [湖南集训]谈笑风生

    题目链接 https://www.lydsy.com/JudgeOnline/problem.php?id=3653 https://www.luogu.org/problemnew/show/P38 ...

  8. 【洛谷 P3899】 [湖南集训]谈笑风生 (主席树)

    题目链接 容易发现\(a,b,c\)肯定是在一条直链上的. 定义\(size(u)\)表示以\(u\)为根的子树大小(不包括\(u\)) 分两种情况, 1.\(b\)是\(a\)的祖先,对答案的贡献是 ...

  9. bzoj 3653 [湖南集训]谈笑风生

    题目描述 设 T 为一棵有根树,我们做如下的定义: • 设 a 和 b 为 T 中的两个不同节点.如果 a 是 b 的祖先,那么称"a 比 b 不知道高明到哪里去了". • 设 a ...

随机推荐

  1. 【Java】socket编程,输入输出中的问题

    BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWri ...

  2. 晚间测试3 B. 单(single)

    题目描述 单车联通大街小巷.这就是出题人没有写题目背景的原因. 对于一棵树,认为每条边长度为 \(1\),每个点有一个权值\(a[i]\).\(dis(u,v)\)为点\(u\)到\(v\)的最短路径 ...

  3. Tomcat 第五篇:请求处理流程(下)

    1. 请求处理流程 AprEndPoint 顺着上一篇接着聊,当一个请求发送到 Tomcat 以后,会由连接器 Connector 转送至 AprEndPoint ,在 AprEndPoint 中调用 ...

  4. P3118 [USACO15JAN]Moovie Mooving G

    P3118 [USACO15JAN]Moovie Mooving G Link 题目描述 Bessie is out at the movies. Being mischievous as alway ...

  5. 099 01 Android 零基础入门 02 Java面向对象 03 综合案例(学生信息管理) 02 案例分析及实现 03 编写并测试Student类

    099 01 Android 零基础入门 02 Java面向对象 03 综合案例(学生信息管理) 02 案例分析及实现 03 编写并测试Student类 本文知识点:编写并测试Subject类 说明: ...

  6. vue显示后端传递的图片流

    一.显示部分(组件我使用的vuetify) <template> <v-container fluid> <v-card width="100%" m ...

  7. Linux系统编程—有名管道

    ▋****1. 管道的概念 管道,又名「无名管理」,或「匿名管道」,管道是一种非常基本,也是使用非常频繁的IPC方式. 1.1 管道本质 管道的本质也是一种文件,不过是伪文件,实际上是一块内核缓冲区, ...

  8. java9系列第二篇-资源自动关闭的语法增强

    我计划在后续的一段时间内,写一系列关于java 9的文章,虽然java 9 不像Java 8或者Java 11那样的核心java版本,但是还是有很多的特性值得关注.期待您能关注我,我将把java 9 ...

  9. 网络最大流Dinic

    1.什么是网络最大流 形象的来说,网络最大流其实就是这样一个生活化的问题:现在有一个由许多水管组成的水流系统,每一根管道都有自己的最大通过水流限制(流量),超过这个限制水管会爆(你麻麻就会来找你喝茶q ...

  10. 闭嘴,给你一个数!1分钟,学完C语言指针,不扎手只扎心的针!

    序言 指针是C语言学习者绕不过的一道坎,也是C语言学习者不得绕过的一道坎.辨别一个人C语言学的好赖就看他对指针的理解怎么样.指针内容也是工作面试经常问到的问题.本文将带你重新认识那个绊倒你的指针,以解 ...