[COGS 2258][HZOI 2015]复仇的序幕曲

Description

你还梦不梦痛不痛,回忆这么重你怎么背得动 ----序言

当年的战火硝烟已经渐渐远去,可仇恨却在阿凯蒂王子的心中越来越深

他的叔父三年前谋权篡位,逼宫杀死了他的父王,用铁血手腕平定了国内所有的不满

只有他一个人孤身逃了出来,而现在他组织了一只强大的军队,反攻的号角已经吹响

大战一触即发,作为他的机智又勇敢的指挥官,你必须要准确及时的完成他布置的任务

这个国家的布局是一棵树,每个城市都是树上的结点,其中每个结点上都有军队ai(人数)

树上的每条边有边权wi,表示通过这条边所需要的时间

当一个城市u受到攻击时,所有城市的军队都会同时向这个城市移动

阿凯蒂王子需要知道在时间T内,u城市最多聚集多少人

Input

第一行n,m,分别表示城市数目和询问次数

第二行有n个正整数,表示每个结点军队人数ai

以下n-1行每行描述树上的一条边的两个端点u,v和边权w

以下m行每行一个询问u,T

表示在时间T内,u城市最多聚集多少人

注意询问之间相互独立

Output

输出m行,每行一个数

表示询问的答案

Sample Input

5 5

3 7 1 7 4

2 1 9

3 1 6

4 2 5

5 3 1

5 1

4 3

1 1

1 4

4 2

Sample Output

5

7

3

3

7

Hint

n<=80000,m<=80000

边权和军队人数均<=1000

题解

首先,对于题目要求 $dist(u, v) \leq k$ ,我们从 $u$ 在点分树向上跳的时候,第一次处理到含点 $v$ 的重心,那么这个重心就是 $lca(u, v)$ ,我们对这个 $lca$ 进行处理。

由于满足上式,所以 $dist(u, lca)+dist(v, lca) \leq k$ 等价于若点 $v$ 满足 $dist(v, lca) \leq k-dist(u, lca)$ 那么显然 $v$ 是满足 $u$ 。

那么我们可以在每个节点上建一棵平衡树,维护以其为重心的子树中 $dist(v, lca)$ 的值,显然统计答案就是平衡树中 $\leq k-dist(u, lca)$ 的个数。

按照套路,为了防止重复计算,我们需要减去会在这个重心的父亲处重复计算的值。记在点分树中 $u$ 节点的父亲为 $fa_u$ ,所以我们再开一个平衡树来存 $dist(fa_{lca}, v)$ ,额外减去的就是第二棵平衡树中 $\leq k-dist(fa_{lca}, u)$ 的个数。

  1 //It is made by Awson on 2018.1.19
  2 #include <set>
  3 #include <map>
  4 #include <cmath>
  5 #include <ctime>
  6 #include <queue>
  7 #include <stack>
  8 #include <cstdio>
  9 #include <string>
 10 #include <vector>
 11 #include <cstdlib>
 12 #include <cstring>
 13 #include <iostream>
 14 #include <algorithm>
 15 #define LL long long
 16 #define Abs(a) ((a) < 0 ? (-(a)) : (a))
 17 #define Max(a, b) ((a) > (b) ? (a) : (b))
 18 #define Min(a, b) ((a) < (b) ? (a) : (b))
 19 #define Swap(a, b) ((a) ^= (b), (b) ^= (a), (a) ^= (b))
 20 #define writeln(x) (write(x), putchar(‘\n‘))
 21 using namespace std;
 22 const int N = 80000;
 23 const int INF = ~0u>>1;
 24 void read(int &x) {
 25     char ch; bool flag = 0;
 26     for (ch = getchar(); !isdigit(ch) && ((flag |= (ch == ‘-‘)) || 1); ch = getchar());
 27     for (x = 0; isdigit(ch); x = (x<<1)+(x<<3)+ch-48, ch = getchar());
 28     x *= 1-2*flag;
 29 }
 30 void write(int x) {
 31     if (x > 9) write(x/10);
 32     putchar(x%10+48);
 33 }
 34
 35 int val[N+5], n, m, u, v, fa[N+5], last, c;
 36 struct tt {int to, next, cost; }edge[(N<<1)+5];
 37 int path[N+5], top;
 38 void add(int u, int v, int c) {
 39     edge[++top].to = v, edge[top].next = path[u], edge[top].cost = c, path[u] = top;
 40 }
 41 struct Treap {
 42     int root[N+5], key[N*30+5], val[N*30+5], sum[N*30+5], lev[N*30+5], ch[N*30+5][2], pos;
 43     void newnode(int &o, int keyy, int vall) {
 44     o = ++pos;
 45     key[o] = keyy, sum[o] = val[o] = vall, lev[o] = rand(), ch[o][0] = ch[o][1] = 0;
 46     }
 47     void pushup(int o) {sum[o] = sum[ch[o][0]]+sum[ch[o][1]]+val[o]; }
 48     void rotate(int &o, int kind) {
 49     int x = ch[o][!kind];
 50     ch[o][!kind] = ch[x][kind];
 51     ch[x][kind] = o;
 52     o = x;
 53     }
 54     void insert(int &o, int keyy, int vall) {
 55     if (!o) {newnode(o, keyy, vall); return; }
 56     sum[o] += vall; int kind = keyy >= key[o];
 57     insert(ch[o][kind], keyy, vall);
 58     if (lev[ch[o][kind]] < lev[o]) rotate(o, !kind), pushup(ch[o][!kind]), pushup(o);
 59     }
 60     int query(int o, int keyy) {
 61      if (!o) return 0;
 62     if (keyy >= key[o]) return sum[ch[o][0]]+val[o]+query(ch[o][1], keyy);
 63     return query(ch[o][0], keyy);
 64     }
 65 }T1, T2;
 66 namespace LCA {
 67     int log2[(N<<1)+5], bin[25], f[(N<<1)+5][25], dfn[N+5], tim;
 68     void dfs(int o, int cost) {
 69     f[dfn[o] = ++tim][0] = cost;
 70     for (int i = path[o]; i; i = edge[i].next)
 71         if (!dfn[edge[i].to]) dfs(edge[i].to, cost+edge[i].cost), f[++tim][0] = cost;
 72     }
 73     int query(int x, int y) {
 74     if (dfn[x] > dfn[y]) Swap(x, y); int lim = log2[dfn[y]-dfn[x]+1];
 75     return Min(f[dfn[x]][lim], f[dfn[y]-bin[lim]+1][lim]);
 76     }
 77     int dist(int x, int y) {return f[dfn[x]][0]+f[dfn[y]][0]-(query(x, y)<<1); }
 78     void main() {
 79     bin[0] = 1, log2[0] = -1;
 80     for (int i = 1; i <= (n<<1); i++) log2[i] = log2[i>>1]+1;
 81     for (int i = 1; i <= 20; i++) bin[i] = bin[i-1]<<1;
 82     dfs(1, 0);
 83     for (int t = 1; t <= log2[n<<1]; t++) for (int i = 1; i+bin[t]-1 <= (n<<1); i++) f[i][t] = Min(f[i][t-1], f[i+bin[t-1]][t-1]);
 84     }
 85 }
 86 namespace Point_divide {
 87     int size[N+5], mx[N+5], minsize, root, vis[N+5];
 88     void get_root(int o, int pa, int fa) {
 89     mx[o] = Max(mx[o], size[pa]-size[o]);
 90     if (mx[o] < minsize) minsize = mx[o], root = o;
 91     for (int i = path[o]; i; i = edge[i].next)
 92         if (edge[i].to != fa && !vis[edge[i].to]) get_root(edge[i].to, pa, o);
 93     }
 94     void get_size(int o, int fa) {
 95     size[o] = 1, mx[o] = 0;
 96     for (int i = path[o]; i; i = edge[i].next)
 97         if (edge[i].to != fa && !vis[edge[i].to]) {
 98         get_size(edge[i].to, o);
 99         size[o] += size[edge[i].to];
100         if (size[edge[i].to] > mx[o]) mx[o] = size[edge[i].to];
101         }
102     }
103     void get_insert(int o, int pa, int da, int fa, int cost) {
104     T1.insert(T1.root[da], cost, val[o]); if (pa) T2.insert(T2.root[da], LCA::dist(pa, o), val[o]);
105     for (int i = path[o]; i; i = edge[i].next) if (edge[i].to != fa && !vis[edge[i].to]) get_insert(edge[i].to, pa, da, o, cost+edge[i].cost);
106     }
107     void work(int o, int pa) {
108     minsize = INF; get_size(o, 0), get_root(o, o, 0); vis[root] = 1, fa[root] = pa; int rt = root;
109     T1.insert(T1.root[root], 0, val[root]); if (pa) T2.insert(T2.root[root], LCA::dist(pa, root), val[root]);
110     for (int i = path[root]; i; i = edge[i].next)
111         if (!vis[edge[i].to]) get_insert(edge[i].to, pa, root, 0, edge[i].cost);
112     for (int i = path[root]; i; i = edge[i].next)
113         if (!vis[edge[i].to]) work(edge[i].to, rt);
114     }
115     void main() {work(1, 0); }
116 }
117
118 int query(int o, int k) {
119     int ans = 0;
120     for (int x = o; x; x = fa[x]) {
121     ans += T1.query(T1.root[x], k-LCA::dist(x, o));
122     if (fa[x]) ans -= T2.query(T2.root[x], k-LCA::dist(fa[x], o));
123     }
124     return ans;
125 }
126 void work() {
127     srand(time(0)); read(n), read(m);
128     for (int i = 1; i <= n; i++) read(val[i]);
129     for (int i = 1; i < n; i++) {read(u), read(v), read(c); add(u, v, c), add(v, u, c); }
130     LCA::main(); Point_divide::main();
131     while (m--) {
132     read(u), read(v); writeln(query(u, v));
133     }
134 }
135 int main() {
136     work();
137     return 0;
138 }

原文地址:https://www.cnblogs.com/NaVi-Awson/p/8315282.html

时间: 2024-10-11 13:57:47

[COGS 2258][HZOI 2015]复仇的序幕曲的相关文章

cogs 2320. [HZOI 2015]聪聪的世界题解

2320. [HZOI 2015]聪聪的世界 时间限制:6 s   内存限制:512 MB [题目描述] 背景: 聪聪的性取向有问题. 题目描述: 聪聪遇到了一个难题: 给出一个序列a1-an,完成以下操作: 1  x 询问从x向左数第一个<ax的数: 2  x 询问从x向左数第一个>ax的数: 3  x 询问从x向右数第一个<ax的数: 4  x 询问从x向右数第一个>ax的数: 5  x y 交换ax与ay: 6  x y w 给ax-ay加上w: 7  x y w 给ax-a

COGS 2188. [HZOI 2015] Math 题解

  题目描述: 给定n个数X1-Xn,求下面式子的值(整数部分): n<=107,xi<=109且互不相同. 分析: 其实一开始看见这道题我也吓傻了,k这么大,再说我又是数论鶸渣,打死也不会= = 后来看了各路神犇的题解,又仔细想了想,大概明白了. 首先,k这么大,已经不是高精乘和高精开方所能承受的了(当然,你也可以找个超级计算机算算试试) 所以我们可以把k视为∞(INF). 极限思想,由于xi互不相同,所以每个元素在比它稍微大一点点的数面前都是微乎其微,不会影响到整数部分的. (可以粗略验证

cogs 2320. [HZOI 2015]聪聪的世界

solution 6 7 8都好说 对于1 2 3 4只需自己yy一个函数就行 (ps:我把L打成l....) 1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #define ll long long 5 using namespace std; 6 const int N=1000006; 7 inline ll maxn(ll a,ll b){return a>b?a:b;} 8 in

cogs 2478. [HZOI 2016]简单的最近公共祖先

2478. [HZOI 2016]简单的最近公共祖先 ★☆   输入文件:easy_LCA.in   输出文件:easy_LCA.out   简单对比时间限制:2 s   内存限制:128 MB [题目描述] 给定一棵有n个节点的有根树,根节点为1,每个节点有一个权值wi,求 即求所有无序节点对的LCA的权值之和. 树的节点编号为1~n,LCA表示两节点的最近公共祖先,即在它们的所有公共祖先中离根节点最远的节点. [输入格式] 第一行一个整数n,表示节点数. 第二行n个正整数,表示每个点的权值.

COGS 2334. [HZOI 2016]最小函数值

时间限制:1 s   内存限制:128 MB [题目描述] 有n个函数,分别为F1,F2,...,Fn.定义Fi(x)=Aix2+Bix+Ci(x∈N∗).给定这些Ai.Bi和Ci,请求出所有函数的所有函数值中最小的m个(如有重复的要输出多个). [输入格式] 第一行输入两个正整数n和m. 以下n行每行三个正整数,其中第i行的三个数分别为Ai.Bi和Ci.输入数据保证Ai<=10,Bi<=100,Ci<=10000. [输出格式] 输出将这n个函数所有可以生成的函数值排序后的前m个元素.

COGS 2416.[HZOI 2016]公路修建 &amp; COGS 2419.[HZOI 2016]公路修建2 题解

大意: [HZOI 2016]公路修建 给定一个有n个点和m-1组边的无向连通图,其中每组边都包含一条一级边和一条二级边(连接的顶点相同),同一组边中的一级边权值一定大于等于二级边,另外给出一个数k(k<=n-1),求原图的一个生成树,使得其中至少包含k条一级边且最大的边权值尽量小. [HZOI 2016]公路修建2 和上一题基本一样,但是求出的不一定是生成树(也就是说可以有多于n-1条边,只要让图连通即可),在此前提下仍然使得其中至少包含k条一级边且最大的边权值尽量小. 解法: 两题均可以使用

cogs 2632. [HZOI 2016] 数列操作d

2632. [HZOI 2016] 数列操作d ★★★   输入文件:segment.in   输出文件:segment.out   简单对比时间限制:3 s   内存限制:512 MB [题目描述] 一个长度为n的序列,一开始序列数的权值都是0,有m次操作 支持两种操作: 1 L R x,给区间[L,R]内位置为pos的数加上(pos-L)*x 0 L R,查询区间[L,R]内的权值和 最终答案对109+7取模. [输入格式] 第一行两个数n,m,表示序列长度和操作次数 接下来m行,每行描述一

cogs 1963. [HAOI 2015] 树上操作 树链剖分+线段树

1963. [HAOI 2015] 树上操作 ★★★☆   输入文件:haoi2015_t2.in   输出文件:haoi2015_t2.out   简单对比时间限制:1 s   内存限制:256 MB [题目描述] 有一棵点数为N的树,以点1为根,且树点有权值.然后有M个操作,分为三种: 操作1:把某个节点x的点权增加a. 操作2:把某个节点x为根的子树中所有点的点权都增加a. 操作3:询问某个节点x到根的路径中所有点的点权和. [输入格式] 第一行两个整数N,M,表示点数和操作数. 接下来一

COGS2287 [HZOI 2015]疯狂的机器人

[题目描述] 现在在二维平面内原点上有一只机器人 他每次操作可以选择向右走,向左走,向下走,向上走和不走(每次如果走只能走一格) 但是由于本蒟蒻施展的大魔法,机器人不能走到横坐标是负数或者纵坐标是负数的点上 否则他就会big bang 给定操作次数n,求有多少种不同的操作序列使得机器人在操作后会回到原点 输出答案模998244353后的结果 注意如果两个操作序列存在某一时刻操作不同,则我们认为这两个操作序列不同 [输入格式] 输入n,表示操作次数 n<=100000 [输出格式] 按要求输出答案