『树的dfs序·线段树』某次模拟赛T2

2023-10-13 12:38
文章标签 模拟 dfs 线段 t2 某次

本文主要是介绍『树的dfs序·线段树』某次模拟赛T2,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

P r o b l e m \mathrm{Problem} Problem

在这里插入图片描述

S o l u t i o n \mathrm{Solution} Solution

我们令 d i s x dis_x disx表示节点 x x x的到根的距离,令 s t e p x step_x stepx表示节点x的深度。

那么对于每一对询问(x,k),我们令终止点为y,则我们要找的是:
min ⁡ k ≤ d i s y − d i s x ( s t e p y ) \min_{k\le dis_y-dis_x} (step_y) kdisydisxmin(stepy)

我们仔细关注条件 d i s y − d i s x ≥ k dis_y-dis_x\ge k disydisxk,也就是 d i s y ≥ d i s x + k dis_y\ge dis_x+k disydisx+k.

我们每一次只需要在节点x的子树内,查找满足上述条件的最小 s t e p step step即可。

关键是如何快速查询呢?由于是子树问题,我们可以使用dfs序对应到区间上,如果我们能将所有满足条件 d i s y ≥ d i s x + k dis_y\ge dis_x+k disydisx+k的子节点y,都将将对应的 s t e p y step_y stepy放到对应的序列上,我们就可以用线段树来维护了。

那么我们可以将若干个询问离线处理,将权值从大到小排序。对于类型1加入权值 d i s x + k dis_x+k disx+k,每一次查询的时候查询子树的最小 s t e p step step值;对于类型2,在对应位置上加入 s t e p step step值即可。

这个离线妙啊

C o d e \mathrm{Code} Code

#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>using namespace std;
const int N = 8e5;int n, cnt(0);
int dis[N], step[N], L[N], R[N], Ans[N];
vector < pair<int, int> > G[N];
struct node{int type, point, val, id;friend bool operator < (node p1,node p2) {if (p1.val == p2.val) return p1.type > p2.type;return p1.val > p2.val;}
} ques[N * 2];
struct Segment {int l, r, min;
} a[N * 2];int read(void)
{int s = 0, w = 0; char c = getchar();while (c < '0' || c > '9') w |= c == '-', c = getchar();while (c >= '0' && c <= '9') s = s*10+c-48, c = getchar();return w ? -s : s;
}void dfs(int x,int fa)
{L[x] = ++ cnt;for (int i=0;i<G[x].size();++i){int y = G[x][i].first;int v = G[x][i].second;if (y == fa) continue;dis[y] = dis[x] + v;step[y] = step[x] + 1;dfs(y,x);		}R[x] = cnt;return;
}void build(int p,int l,int r)
{a[p].l = l, a[p].r = r, a[p].min = 1e9;if (l == r) return;int mid = l + r >> 1;build(p*2,l,mid);build(p*2+1,mid+1,r);return;
}void change(int p,int x,int v) 
{if (a[p].l == x && a[p].r == x) {a[p].min = min(a[p].min,v);return;}int mid = a[p].l + a[p].r >> 1;if (x <= mid) change(p*2,x,v);if (x > mid) change(p*2+1,x,v);a[p].min = min(a[p*2].min,a[p*2+1].min);return;
}int ask(int p,int l,int r)
{if (l <= a[p].l && r >= a[p].r) return a[p].min;int mid = a[p].l + a[p].r >> 1, ret = 1e9;if (l <= mid) ret = min(ret,ask(p*2,l,r));if (r > mid) ret = min(ret,ask(p*2+1,l,r));return ret;
}int main(void)
{freopen("b.in","r",stdin);freopen("b.out","w",stdout);read(), n = read();for (int i=1,x,y,v;i<n;++i){x = read(), y = read(), v = read();G[x].push_back({y,v});G[y].push_back({x,v});}dfs(1,0);int q = read();for (int i=1;i<=q;++i) {int x = read(), k = read();ques[i] = node{1,x,dis[x]+k,i};}for (int i=1;i<=n;++i) ques[q+i] = node{2,i,dis[i],0};sort(ques+1,ques+q+n+1);build(1,1,n);for (int i=1,ans;i<=q+n;++i){int type(ques[i].type), point(ques[i].point), Id(ques[i].id);if (type == 1)  Ans[Id] = ask(1,L[point],R[point])-step[point];else change(1,L[point],step[point]);}for (int i=1;i<=q;++i)printf("%d\n", Ans[i] > n ? -1 : Ans[i]);return 0;
}

这篇关于『树的dfs序·线段树』某次模拟赛T2的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/203274

相关文章

poj3468(线段树成段更新模板题)

题意:包括两个操作:1、将[a.b]上的数字加上v;2、查询区间[a,b]上的和 下面的介绍是下解题思路: 首先介绍  lazy-tag思想:用一个变量记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率。 比如现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行操作,如果刚好执行到一个子节点,

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

hdu1689(线段树成段更新)

两种操作:1、set区间[a,b]上数字为v;2、查询[ 1 , n ]上的sum 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdl

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

usaco 1.2 Transformations(模拟)

我的做法就是一个一个情况枚举出来 注意计算公式: ( 变换后的矩阵记为C) 顺时针旋转90°:C[i] [j]=A[n-j-1] [i] (旋转180°和270° 可以多转几个九十度来推) 对称:C[i] [n-j-1]=A[i] [j] 代码有点长 。。。 /*ID: who jayLANG: C++TASK: transform*/#include<

hdu 1754 I Hate It(线段树,单点更新,区间最值)

题意是求一个线段中的最大数。 线段树的模板题,试用了一下交大的模板。效率有点略低。 代码: #include <stdio.h>#include <string.h>#define TREE_SIZE (1 << (20))//const int TREE_SIZE = 200000 + 10;int max(int a, int b){return a > b ? a :

hdu 1166 敌兵布阵(树状数组 or 线段树)

题意是求一个线段的和,在线段上可以进行加减的修改。 树状数组的模板题。 代码: #include <stdio.h>#include <string.h>const int maxn = 50000 + 1;int c[maxn];int n;int lowbit(int x){return x & -x;}void add(int x, int num){while

hdu 2489 (dfs枚举 + prim)

题意: 对于一棵顶点和边都有权值的树,使用下面的等式来计算Ratio 给定一个n 个顶点的完全图及它所有顶点和边的权值,找到一个该图含有m 个顶点的子图,并且让这个子图的Ratio 值在所有m 个顶点的树中最小。 解析: 因为数据量不大,先用dfs枚举搭配出m个子节点,算出点和,然后套个prim算出边和,每次比较大小即可。 dfs没有写好,A的老泪纵横。 错在把index在d

poj 3050 dfs + set的妙用

题意: 给一个5x5的矩阵,求由多少个由连续6个元素组成的不一样的字符的个数。 解析: dfs + set去重搞定。 代码: #include <iostream>#include <cstdio>#include <set>#include <cstdlib>#include <algorithm>#include <cstring>#include <cm

poj 1127 线段相交的判定

题意: 有n根木棍,每根的端点坐标分别是 px, py, qx, qy。 判断每对木棍是否相连,当他们之间有公共点时,就认为他们相连。 并且通过相连的木棍相连的木棍也是相连的。 解析: 线段相交的判定。 首先,模板中的线段相交是不判端点的,所以要加一个端点在直线上的判定; 然后,端点在直线上的判定这个函数是不判定两个端点是同一个端点的情况的,所以要加是否端点相等的判断。 最后