【NOI模拟】谈笑风生(主席树)
题目描述
设 T 为一棵有根树,我们做如下的定义:
- 设 a 和 b 为 T 中的两个不同节点。如果 a 是 b 的祖先,那么称 “ a 比 b 不知道高明到哪里去了 ” 。
- 设 a 和 b 为 T 中的两个不同节点。如果 a 与 b 在树上的距离不超过某个给定常数 x ,那么称 “ a 与 b 谈笑风生 ” 。
给定一棵 n 个节点的有根树 T ,节点的编号为 1~n ,根节点为 1 号节点。你需要回答 q 个询问,询问给定两个整数 p 和 k ,问有多少个有序三元组 (a,b,c) 满足:
1. a、b 和 c 为 T 中三个不同的点,且 a 为 p 号节点;
2. a 和 b 都比 c 不知道高明到哪里去了;
3. a 和 b 谈笑风生。这里谈笑风生中的常数为给定的 k 。
输入格式
输入的第一行含有两个正整数n和q,分别代表有根树的点数与询问的个数。
接下来 n-1 行,每行描述一条树上的边。每行含有两个整数 u 和 v ,代表在节点 u 和 v 之间有一条边。
接下来 q 行,每行描述一个操作。第 i 行含有两个整数,分别表示第 i 个询问的 p 和 k 。
输出格式
输出 q 行,每行对应一个询问,代表询问的答案。
样例输入
5 3
1 2
1 3
2 4
4 5
2 2
4 1
2 3
样例输出
3
1
3
题目分析
下图是样例数据:
对于询问2 2, b可以取1或4
- b取1时, c可以取4, 5:
ans += 1 * 2
- b取4时, c只能取5:
ans += 1 * 1
- 综上ans = 3
- 可以发现对于询问a,k: 若根节点的dep为0, sze[i]表示以i为根的树的大小
- b可以在根节点到a的路径上,此时c可以是a子树中的任意节点 ans += min(dep[a], k) * (sze[a] - 1)
- b可以是a的子树节点中且与a的距离小于等于k, 对于每一个b, c可以是b子树结点中的任意一点
$$ans += \sum_{b} sze[b] - 1$$
- 由此, 先dfs出树的dep,sze
然后需要加上a子树结点中与a距离小于等于k的节点的子树大小之和.
- 我们可以使用可持久化线段树来完成这一任务。我们对深度建线段树,按照 DFS 序的
顺序依次插入每个点。这样就可以通过全部的测试点。
- 具体可以看代码;
CODE:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
using namespace std; typedef long long ll;
const int N = ;
int n, q;
#define bug(x) cout<<#x<<":"<<x<<endl inline int Re(){
int i = , f = ; char ch = getchar();
for(; (ch < '' || ch > '') && ch != '-'; ch = getchar());
if(ch == '-') f = -, ch = getchar();
for(; ch >= '' && ch <= ''; ch = getchar())
i = (i << ) + (i << ) + (ch - '');
return i * f;
} struct node{
int lc, rc;
ll sum;
}tr[N << ];
int pool, root[N], sze[N], dep[N], in[N], out[N], id; int ecnt, adj[N], go[N << ], nxt[N << ], maxx, pos[N]; inline void Insert(const int &x, int &y, const int &l, const int &r, const int &pos, const int &v){
tr[y = ++pool] = tr[x];
tr[y].sum += v;
if(l == r) return;
int mid = l + r >> ;
if(pos <= mid) Insert(tr[x].lc, tr[y].lc, l, mid, pos, v);
else Insert(tr[x].rc, tr[y].rc, mid + , r, pos, v);
} inline ll query(int pos, const int &nl, const int &nr, const int &l, const int &r){
if(l <= nl && nr <= r) return tr[pos].sum;
int mid = nl + nr >> ;
ll ret = ;
if(l <= mid) ret += query(tr[pos].lc, nl, mid, l, r);
if(r > mid ) ret += query(tr[pos].rc, mid + , nr, l, r);
return ret;
} inline void addEdge(const int &u, const int &v){
nxt[++ecnt] = adj[u], adj[u] = ecnt, go[ecnt] = v;
nxt[++ecnt] = adj[v], adj[v] = ecnt, go[ecnt] = u;
} inline void dfs(const int &u, const int &f){
dep[u] = dep[f] + ;
maxx = max(maxx, dep[u]);
sze[u] = ;
in[u] = ++id;
pos[id] = u;
int v;
for(int e = adj[u]; e; e = nxt[e]){
if((v = go[e]) == f) continue;
dfs(v, u);
sze[u] += sze[v];
}
out[u] = id;
} inline void tree_init(){
for(int i = ; i <= n; i++){
Insert(root[i - ], root[i], , maxx, dep[pos[i]], sze[pos[i]] - );
}
} int main(){
freopen("h.in", "r", stdin);
n = Re(), q = Re();
for(int i = ; i < n; i++){
int u = Re(), v = Re();
addEdge(u, v);
}
maxx = -; dep[] = -;
dfs(, );
tree_init();
// for(int i = 1; i <= n; i++) bug(i),bug(dep[pos[i]]);
for(int i = ; i <= q; i++){
int p = Re();
int k = Re();
ll ans = ;
ans += (ll)(sze[p] - ) * (ll)min(dep[p], k);
// bug(ans);
ans += query(root[out[p]], , maxx, min(dep[p] + , maxx), min(dep[p] + k, maxx));
// bug(ans); bug(query(root[out[p]], 1, maxx, min(dep[p] + 1, maxx), min(dep[p] + k, maxx)));
ans -= query(root[in[p] - ], , maxx, min(dep[p] + , maxx), min(dep[p] + k, maxx));
cout<<ans<<endl;
}
return ;
}
【NOI模拟】谈笑风生(主席树)的更多相关文章
- bzoj 3653 谈笑风生——主席树
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3653 原来一直想怎么线段树合并.可是不会把角标挪一位. 查询的其实是子树内一段深度的点的 s ...
- bzoj 3653 谈笑风生 —— 主席树
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3653 对于一个 (a,b,c),分成 b 是 a 的祖先和 b 在 a 子树里两部分: 第一 ...
- [BZOJ3653]谈笑风生 主席树
题面 这道题应该比较裸吧. \(a\),\(b\)都是\(c\)的祖先. 那么第一种情况是\(b\)是\(a\)的祖先,那么方案数就是\(\min\{dep[a]-1,k\}\cdot (num[a] ...
- BZOJ 3653: 谈笑风生(主席树)
传送门 解题思路 首先对于一个\(a\)来说,要求\(b\)和\(c\),那么\(a,b,c\)一定在一条链上.把\(b\)分类讨论,如果\(b\)是\(a\)的祖宗,这个方案数就很好统计了,就是\( ...
- P3899 [湖南集训]谈笑风生 主席树
#include<iostream> #include<string.h> #include<algorithm> #include<stdio.h> ...
- [Luogu P3899] [湖南集训]谈笑风生 (主席树)
题面 传送门:https://www.luogu.org/problemnew/show/P3899 Solution 你们搞的这道题啊,excited! 这题真的很有意思. 首先,我们可以先理解一下 ...
- Newnode's NOI(P?)模拟赛 第三题 (主席树优化建图 + tarjan)
题目/题解戳这里 这道题题目保证a,b,ca,b,ca,b,c各是一个排列-mdzz考场上想到正解但是没看到是排列,相等的情况想了半天-然后写了暴力60分走人- 由于两两间关系一定,那么就是一个竞赛图 ...
- 数据结构(主席树):COGS 2211. 谈笑风生
2211. 谈笑风生 ★★★★ 输入文件:laugh.in 输出文件:laugh.out 简单对比时间限制:3 s 内存限制:512 MB [问题描述] 设T 为一棵有根树,我们做如下 ...
- 主席树 || 可持久化线段树 || BZOJ 3653: 谈笑风生 || Luogu P3899 [湖南集训]谈笑风生
题面:P3899 [湖南集训]谈笑风生 题解: 我很喜欢这道题. 因为A是给定的,所以实质是求二元组的个数.我们以A(即给定的P)作为基点寻找答案,那么情况分两类.一种是B为A的父亲,另一种是A为B的 ...
随机推荐
- Unity应用架构设计(11)——一个网络层的构建
对于客户端应用程序,免不了和远程服务打交道.设计一个良好的『服务层』能帮我们规范和分离业务代码,提高生产效率.服务层最核心的模块一定是怎样发送请求,虽然Mono提供了很多C#网络请求类,诸如WebCl ...
- springMVC+Mybatis(使用AbstractRoutingDataSource实现多数据源切换时)事务管理未生效的解决办法
业务场景: A.B两个单位,系统部署同一套代码: A.B两系统能相互访问: 要求将数据从A系统同步到B系统,再将反馈信息回发给A: 实际开发情况: 因为系统比较小,最开始设计架构的时候没有考虑到消息互 ...
- dedecms的热门标签在那里修改
很多人都在用dedecms,因为它不但开源,而且功能还很强大.有会员功能,评论功能,问答功能,积分功能,充值卡等.那么我们来看看很多同学在优黔图里面的提的问题-dedecms的热门标签在那里修改? 其 ...
- LoadRunner11_录制Oracle数据库脚本
[oracle环境] ①oracle:无需在本地安装oracle,但是oracle的odbc驱动一定要装:(我的安装路径为 D:\oracle ).安装好后在环境变量 " Path &quo ...
- Python原理 -- 深浅拷贝
python原理 -- 深浅拷贝 从数据类型说开去 str, num : 一次性创建, 不能被修改, 修改即是再创建. list,tuple,dict,set : 链表,当前元素记录, 下一个元素的位 ...
- ip地址0.0.0.0与127.0.0.1的区别(转载)
原文链接:http://blog.csdn.net/ttx_laughing/article/details/58586907 最近在项目开发中发现一个奇怪的问题,当服务器与客户端在同一台机器上时,用 ...
- 理解Hibernate事务机制,首先需要搞清楚的6个问题
问题1:到底该用getTransaction还是beginTransaction? 上图说明的问题: 第1步,调用session.getTransaction()的时候,会创建一个全新的Transac ...
- Java锁Synchronized,对象锁和类锁举例
Java的锁分为对象锁和类锁. 1. 当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内针对该对象的操作只能有一个线程得到执行.另一个线程必须 ...
- [leetcode-535-Encode and Decode TinyURL]
TinyURL is a URL shortening service where you enter a URL such as https://leetcode.com/problems/desi ...
- h1b期间回国须知
今天才搞明白几点 1. visa 和 status 是两个不同的东西,status能保证合法在美国.visa能保证合法进入美国 所以,h1b十月份的身份转换时status的转换,如果回国还需要重新办h ...