EC Round 33 F. Subtree Minimum Query 主席树/线段树合并
这题非常好!!!
主席树版本
很简单的题目,给一个按照指定节点的树,树上有点权,你需要回答给定节点的子树中,和其距离不超过k的节点中,权值最小的。
肯定首先一想,按照dfs序列建树,然后按照深度为下标,建立主席树,那么我们通过主席树相间得到区间状态,但是很不幸,区间最值不能通过减去历史版本的主席树得到。
考虑照深度建立主席树,按照dfs下标建立,貌似可以耶!!!
我们直接查询当前节点往下k深度的主席树,它保存的就是从深度为1-到深度为deep[p]+k深度的所有节点的dfs序对应的点权值
我们查询子树对应区间的dfs序的最小值,就是答案啦!!!
主席树的话,不建议最开始去建树初始化,本来就是动态开点了,不用这么麻烦,这个题也是一样,我们建立主席树的时候,直接写一个析构函数初始化最大值即可
不用再buildtree了2333。。。
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 1e5+;
struct node{
int l,r;
int w;
node(){
w=INF;
}
}tree[maxn*];
struct ID{
int pre,bac;
}id[maxn];
int cnt,tot,dfsorder,mxdep;
int root[maxn*];
int a[maxn],deepth[maxn];
int ver[maxn*],Next[maxn*],head[maxn];
int mp[maxn*];
queue<int>q;
int n,r;
void add(int x,int y){
ver[++tot]=y;Next[tot]=head[x];head[x]=tot;
ver[++tot]=x;Next[tot]=head[y];head[y]=tot;
}
void dfs(int u,int fa){
deepth[u]=deepth[fa]+;
id[u].pre=++dfsorder;
mxdep=max(mxdep,deepth[u]);
for(int i=head[u];i;i=Next[i]){
int v=ver[i];
if(v==fa)continue;
dfs(v,u);
}
id[u].bac=dfsorder;
} void inserts(int l,int r,int pre,int &now,int pos,int w){
now=++cnt;
tree[now]=tree[pre];
tree[now].w=min(tree[now].w,w);
if(l==r){
return;
}
int mid=(l+r)>>;
if(pos<=mid){
inserts(l,mid,tree[pre].l,tree[now].l,pos,w);
}else {
inserts(mid+,r,tree[pre].r,tree[now].r,pos,w);
}
}
int query(int rt,int l,int r,int ql,int qr){
if(ql<=l && r<=qr){
return tree[rt].w;
}
int mid=(l+r)>>;
if (qr<=mid){
return query(tree[rt].l,l,mid,ql,qr);
}else if(ql>mid){
return query(tree[rt].r,mid+,r,ql,qr);
}else {
return min(query(tree[rt].l,l,mid,ql,mid),query(tree[rt].r,mid+,r,mid+,qr));
}
}
void bfs(int s){
q.push(s);
int tmp=;
while(q.size()){
int now=q.front();
q.pop();
inserts(,*n,root[tmp],root[tmp+],id[now].pre,a[now]);
mp[deepth[now]]=++tmp;
for (int i=head[now];i;i=Next[i]){
int nex=ver[i];
if(deepth[nex]==deepth[now]+){
q.push(nex);
}
}
}
}
int main(){
int uu,vv;
while(~scanf("%d%d",&n,&r)){
tot=;
cnt=;
dfsorder=;
mxdep=;
for(int i=;i<=n;i++){
scanf("%d",&a[i]);
}
for (int i=;i<=n-;i++){
scanf("%d%d",&uu,&vv);
add(uu,vv);
}
dfs(r,);
bfs(r);
int op;
int p,q;
int ans=;
scanf("%d",&op);
while(op--){
scanf("%d%d",&p,&q);
p=(p+ans)%n+,q=(q+ans)%n;
ans=query(root[mp[min(deepth[p]+q,mxdep)]],,n*,id[p].pre,id[p].bac);
printf("%d\n",ans);
}
}
return ;
}
线段树合并方法
在机房搞搞瞎搞了半天,看了半天没怎么懂线段树合并,属实菜。。。
最后再瞄了一眼,嗯,貌似懂了,不就是从每个节点都新建一个线段树,然后两个树,暴力对这两个线段树的每个节点,都去 暴力比较取最小值后,再拆掉以前的节点,用新建节点保存合并之后的信息,然后父亲节点的线段树得到儿子节点的信息。然后?然后就没了哈哈哈哈。
然后这道题就变成一道模版题了,子树的信息可以通过合并得到,而线段树保存的就是以深度为下标的儿子节点的点权最小值。询问的时候,我们只需要询问当前节点的线段树,其线段树内部就包含了儿子节点的信息,然后我们在给定的深度区间进行区间询问最小值,就能得到答案。真~模版题,注意这种线段树合并,由于每个节点都要开线段树,大佬说空间接近o(n*logn)所以线段树还是*40吧。
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxx = 1e5+;
struct node{
int l,r;
int w;
node(){
w=INF;
}
}tree[maxx*];
int cnt,tot,maxdeep;
int root[maxx];
int deepth[maxx],ver[maxx*],Next[maxx*],head[maxx];
int a[maxx];
int n,r;
void add(int x,int y){
ver[++tot]=y;Next[tot]=head[x];head[x]=tot;
ver[++tot]=x;Next[tot]=head[y];head[y]=tot;
}
void inserts(int &rt,int l,int r,int pos,int w){
rt=++cnt;
tree[rt].w=min(tree[rt].w,w);
if(l==r){
return ;
}
int mid=(l+r)>>;
if(pos<=mid)inserts(tree[rt].l,l,mid,pos,w);
else inserts(tree[rt].r,mid+,r,pos,w);
}
int merge(int x,int y){
if(!x||!y){
return x+y;
}
int tmp=++cnt;
tree[tmp].l=merge(tree[x].l,tree[y].l);
tree[tmp].r=merge(tree[x].r,tree[y].r);
tree[tmp].w=min(tree[x].w,tree[y].w);
return tmp;
}
int query(int rt,int l,int r,int ql,int qr){
if(ql<=l && r<=qr){
return tree[rt].w;
}
int mid=(l+r)>>;
if(qr<=mid){
return query(tree[rt].l,l,mid,ql,qr);
}else if(ql>mid){
return query(tree[rt].r,mid+,r,ql,qr);
}else{
return min(query(tree[rt].l,l,mid,ql,qr),query(tree[rt].r,mid+,r,ql,qr));
}
}
void dfs(int u,int fa){
deepth[u]=deepth[fa]+;
maxdeep=max(maxdeep,deepth[u]);
inserts(root[u],,n,deepth[u],a[u]);
for (int i=head[u];i;i=Next[i]){
int v=ver[i];
if(v==fa)continue;
dfs(v,u);
root[u]=merge(root[u],root[v]);
}
}
int main(){
int uu,vv;
while(~scanf("%d%d",&n,&r)){
maxdeep=;
cnt=;
tot=;
for(int i=;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=;i<n;i++){
scanf("%d%d",&uu,&vv);
add(uu,vv);
}
int op;
scanf("%d",&op);
dfs(r,);
int ans=;
int p,q;
while(op--){
scanf("%d%d",&p,&q);
p=(p+ans)%n+,q=(q+ans)%n;
ans=query(root[p],,n,deepth[p],min(deepth[p]+q,n));
printf("%d\n",ans);
}
}
return ;
}
EC Round 33 F. Subtree Minimum Query 主席树/线段树合并的更多相关文章
- [cf contest 893(edu round 33)] F - Subtree Minimum Query
[cf contest 893(edu round 33)] F - Subtree Minimum Query time limit per test 6 seconds memory limit ...
- Educational Codeforces Round 33 (Rated for Div. 2) F. Subtree Minimum Query(主席树合并)
题意 给定一棵 \(n\) 个点的带点权树,以 \(1\) 为根, \(m\) 次询问,每次询问给出两个值 \(p, k\) ,求以下值: \(p\) 的子树中距离 \(p \le k\) 的所有点权 ...
- CF893F Subtree Minimum Query 主席树
如果是求和就很好做了... 不是求和也无伤大雅.... 一维太难限制条件了,考虑二维限制 一维$dfs$序,一维$dep$序 询问$(x, k)$对应着在$dfs$上查$[dfn[x], dfn[x] ...
- Codeforces 893F - Subtree Minimum Query
893F - Subtree Minimum Query 题意 给出一棵树,每次询问 \(x\) \(k\),求以 \(x\) 为根结点的子树中的结点到结点 \(x\) 的距离小于等于 \(k\) 的 ...
- CF893F Subtree Minimum Query 解题报告
CF893F Subtree Minimum Query 输入输出格式 输入格式: The first line contains two integers \(n\) and \(r\) ( \(1 ...
- Codeforces Round #538 (Div. 2) F 欧拉函数 + 区间修改线段树
https://codeforces.com/contest/1114/problem/F 欧拉函数 + 区间更新线段树 题意 对一个序列(n<=4e5,a[i]<=300)两种操作: 1 ...
- HDU 1394 Minimum Inversion Number(线段树求最小逆序数对)
HDU 1394 Minimum Inversion Number(线段树求最小逆序数对) ACM 题目地址:HDU 1394 Minimum Inversion Number 题意: 给一个序列由 ...
- AtCoder Regular Contest 069 F Flags 二分,2-sat,线段树优化建图
AtCoder Regular Contest 069 F Flags 二分,2-sat,线段树优化建图 链接 AtCoder 大意 在数轴上放上n个点,点i可能的位置有\(x_i\)或者\(y_i\ ...
- Codeforces Round #620 F2. Animal Observation (hard version) (dp + 线段树)
Codeforces Round #620 F2. Animal Observation (hard version) (dp + 线段树) 题目链接 题意 给定一个nm的矩阵,每行取2k的矩阵,求总 ...
随机推荐
- 2019.8.10 NOIP模拟测试16 反思总结【基本更新完毕忽视咕咕咕】
一如既往先放代码,我还没开始改… 改完T1滚过来了,先把T1T2的题解写了[颓博客啊] 今天下午就要走了,没想到还有送行的饯别礼,真是欣喜万分[并没有] 早上刚码完前面的总结,带着不怎么有希望的心情开 ...
- Leetcode617.Merge Two Binary Trees合并二叉树
给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠. 你需要将他们合并为一个新的二叉树.合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 ...
- 【python爬虫】加密代理IP的使用与设置一套session请求头
1:代理ip请求,存于redis: # 请求ip代理连接,更新redis的代理ip def proxy_redis(): sr = redis.Redis(connection_pool=Pool) ...
- Python3.7.4入门-4模块
4 模块 Python有一种方法可以把定义放在一个文件里,并在脚本或解释器的交互式实例中使用它们.这样的文件被称作 模块 :模块中的定义可以 导入 到其它模块或者 主 模块 模块是一个包含Python ...
- $(window).scrollTop() == $(document).height() - $(window).height()(底端)
jQuery(window).height()代表了当前可见区域的大小,而jQuery(document).height()则代表了整个文档的高度,可视具体情况使用. 注意当浏览器窗口大小改变时(如最 ...
- 如何用好消息推送(push)做APP运营
作为移动端APP产品运营最重要的运营手段,消息推送(push)被越来越多的APP厂商所重视,在信息泛滥的移动互联网时代,手机APP应用安装得越来越多,小小的手机屏幕每天收到的消息推送也越来越多,站在用 ...
- 【记录bug】npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.7 (node_modules\fsvents): npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents1.2.7: wanted {"os":"darwin
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.7 (node_modules\fsvents): npm WARN nots ...
- 重磅!容器集群监控利器 阿里云Prometheus 正式免费公测
Prometheus 作为容器生态下集群监控的首选方案,是一套开源的系统监控报警框架.它启发于 Google 的 borgmon 监控系统,并于 2015 年正式发布.2016 年,Prometheu ...
- 阿里小二的日常工作要被TA们“接管”了!
昨天有人偷偷告诉我说 阿里巴巴其实是一家科技公司! 我想了整整一夜 究竟是谁走漏了风声 那么重点来了,阿里到底是如何在内部的办公.生活中,玩转“黑科技”的呢? AI取名:给你专属的“武侠”花名 花名是 ...
- Linux进程管理(四、 进程终结)
进程调用exit()会终结当前进程,可以显式调用,也可以隐式: c语言main函数结束时编译器会自动加入exit调用 exit是系统调用,对应内核里的sys_exit() -> do_exit( ...