[Luogu P3899] [湖南集训]谈笑风生 (主席树)
题面
传送门: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] [湖南集训]谈笑风生 (主席树)的更多相关文章
- P3899 [湖南集训]谈笑风生 主席树
#include<iostream> #include<string.h> #include<algorithm> #include<stdio.h> ...
- luogu P3899 [湖南集训]谈笑风生 线段树合并
Code: #include<bits/stdc++.h> #define maxn 300002 #define ll long long using namespace std; vo ...
- 主席树 || 可持久化线段树 || BZOJ 3653: 谈笑风生 || Luogu P3899 [湖南集训]谈笑风生
题面:P3899 [湖南集训]谈笑风生 题解: 我很喜欢这道题. 因为A是给定的,所以实质是求二元组的个数.我们以A(即给定的P)作为基点寻找答案,那么情况分两类.一种是B为A的父亲,另一种是A为B的 ...
- luogu P3899 [湖南集训]谈笑风生
传送门 nmyzd,mgdhls,bnmbzdgdnlql,a,wgttxfs 对于一个点\(a\),点\(b\)只有可能是他的祖先或者在\(a\)子树里 如果点\(b\)是\(a\)祖先,那么答案为 ...
- 洛谷P3899 [湖南集训]谈笑风生(线段树合并)
题意 题目链接 Sol 线段树合并板子题,目前我看到两种写法,分别是这样的. 前一种每次需要新建一个节点,空间是\(O(4nlogn)\) 后者不需要新建,空间是\(O(nlogn)\)(面向数据算空 ...
- Luogu 3899 [湖南集训]谈笑风生
BZOJ 3653权限题. 这题方法很多,但我会的不多…… 给定了$a$,我们考虑讨论$b$的位置: 1.$b$在$a$到根的链上,那么这样子$a$的子树中的每一个结点(除了$a$之外)都是可以成为$ ...
- P3899 [湖南集训]谈笑风生
题目链接 https://www.lydsy.com/JudgeOnline/problem.php?id=3653 https://www.luogu.org/problemnew/show/P38 ...
- 【洛谷 P3899】 [湖南集训]谈笑风生 (主席树)
题目链接 容易发现\(a,b,c\)肯定是在一条直链上的. 定义\(size(u)\)表示以\(u\)为根的子树大小(不包括\(u\)) 分两种情况, 1.\(b\)是\(a\)的祖先,对答案的贡献是 ...
- bzoj 3653 [湖南集训]谈笑风生
题目描述 设 T 为一棵有根树,我们做如下的定义: • 设 a 和 b 为 T 中的两个不同节点.如果 a 是 b 的祖先,那么称"a 比 b 不知道高明到哪里去了". • 设 a ...
随机推荐
- Centos-目录或文件拷贝-cp
cp 拷贝或者备份文件或者目录 相关选项 -a 拷贝目录保存文件所有信息 -r 递归拷贝目录 -d 保留连接 -p 保留修改时间和存取权限 -i 有同名提醒是否覆盖 相关应用 1. 备份文件 cp ...
- 数论(8):min_25 筛(扩展埃氏筛)
min_25 筛介绍 我们考虑这样一个问题. \[ans=\sum_{i = 1}^nf(i)\\ \] 其中 \(1 \le n \le 10^{10}\) 其中 \(f(i)\) 是一个奇怪的函数 ...
- 02 C语言最简单快速上手的IDE
不要让开发环境 成为学习路上的拦路虎,先培养学习兴趣和学习路上的成就感,这个对于激励自己持续学习很重要. 等真正从小白入门了,甚至成为大牛了,能解决诸多困难问题了,安装个开发环境还会再是个什么难事吗? ...
- P2832 行路难
题面 Link 题目背景 小X来到了山区,领略山林之乐.在他乐以忘忧之时,他突然发现,开学迫在眉睫 题目描述 山区有 \(n\) 座山.山之间有 \(m\) 条羊肠小道,每条连接两座山,只能单向通过, ...
- Linux执行脚本让进程挂掉后自动重启
1 创建循环监听脚本 autostart.sh 例: 其中futures-market-server-v3andwebsoket.jar 是要监听的执行程序 #/bin/bashwhile true ...
- crontab极简教程
目录 crontab简介 crontab常用命令 示例 crontab简介 Linux crontab是用来定期执行程序的命令. 当安装完成操作系统之后,默认便会启动此任务调度命令. crontab常 ...
- Android开发教程之密码框右侧显示小眼睛
现在都说互联网寒冬,其实只要自身技术能力够强,咱们就不怕!我这边专门针对Android开发工程师整理了一套[Android进阶学习视频].[全套Android面试秘籍].[Android知识点PDF] ...
- Jmeter请求之cookie处理方式
方法一:增加cookie管理器线程组->配置元件->HTTP Cookie管理器,放在最上方 但该方法不一定有效 方法二:http信息头&正则表达式提取器结合使用, 在登录的htt ...
- logback.xml demo
如何关闭 org.apache.zookeeper.clientcnxn 的(控制台大量输出)debug 日志 1.在项目resources路径下新建 logback.xml 2.然后把下面的代码co ...
- monolog handler用哪个
Handlers 记录日志到文件与系统日志(syslog) StreamHandler:记录日志到任何 PHP stream,用它来记录到文件. RotatingFileHandler: 每天一个文件 ...