首页 > 其他分享 >P1600 [NOIP2016 提高组] 天天爱跑步

P1600 [NOIP2016 提高组] 天天爱跑步

时间:2022-09-28 10:22:45浏览次数:79  
标签:NOIP2016 old int bucket2 dep 跑步 P1600 bucket1 include

P1600 NOIP2016 提高组 天天爱跑步
LCA + 桶

点击查看代码
// 
/*
考虑上行的情况
(u, v) 中 u 被 i 看到
<=> 1. u ∈ {i的子树}
	2. lca(u, v) 不属于 {i的子树}
	3. dep[u] = w[i] + dep[i]
bucket1[x]: dep[u] = x 的 u 有多少个
考虑下行的情况
(u, v) 中 v 被 i 看到
<=> 1. v ∈ {i的子树}
    2. lca(u, v) 不属于 {i的子树}
    3. dep[u] - 2 * dep[lca(u, v)] = w[i] - dep[i]
bucket2[x]: dep[u] - 2 * dep[lca(u, v)] = x 的 u 有多少个
*/
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <utility>
#include <array>
#include <queue>

using namespace std;

const int N = 3e5 + 5, M = N << 1, logN = 25;

int n, m;
int h[N], e[M], nxt[M], idx;
int w[N], s[N], t[N]; // w 为观察者出现的时间, s 为玩家的开始节点, t 为玩家的终止节点
int ans[N]; // ans 为每个观察者能看到的人
int f[N][logN], dep[N];

struct bucket_t { // 桶
	int val[N * 2];
	bucket_t() { memset(val, 0, sizeof(val)); }
	inline int &operator [] (const int &i) { return val[i + N]; }
} bucket1, bucket2;

struct Operation {
	int val, t; // t ∈ {1, -1} 为树上差分的操作
};
vector<Operation> oper1[N], oper2[N]; // 分别为上行的操作和下行的操作

void add(int a, int b) {
	e[++ idx] = b, nxt[idx] = h[a], h[a] = idx;
}

void dfs(int u) {
	for(int i = 1; i < logN; i ++)
		if(f[u][i - 1]) f[u][i] = f[f[u][i - 1]][i - 1];
		else break;
	for(int i = h[u]; i; i = nxt[i]) {
		int v = e[i];
		if(v == f[u][0]) continue;
		f[v][0] = u, dep[v] = dep[u] + 1;
		dfs(v);
	}
}

void dfs1(int u) { // 处理上行
	int old = bucket1[w[u] + dep[u]];
	for(int i = h[u]; i; i = nxt[i]) {
		int v = e[i];
		if(v != f[u][0]) dfs1(v);
	}
	for(auto &o : oper1[u]) bucket1[o.val] += o.t;
	ans[u] += bucket1[w[u] + dep[u]] - old; // 新的
}

void dfs2(int u) { // 处理下行
	int old = bucket2[w[u] - dep[u]];
	for(int i = h[u]; i; i = nxt[i]) {
		int v = e[i];
		if(v != f[u][0]) dfs2(v);
	}
	for(auto &o : oper2[u]) bucket2[o.val] += o.t;
	ans[u] += bucket2[w[u] - dep[u]] - old;
}

int LCA(int x, int y) {
	if(dep[x] < dep[y]) swap(x, y);
	for(int i = logN - 1; i >= 0; i --)
		if(dep[f[x][i]] >= dep[y]) x = f[x][i];
	if(x == y) return x;
	for(int i = logN - 1; i >= 0; i --)
		if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
	return f[x][0];
}

int main() {
	scanf("%d%d", &n, &m);
	for(int i = 1, a, b; i < n; i ++)
		scanf("%d%d", &a, &b), add(a, b), add(b, a);
	for(int i = 1; i <= n; i ++) scanf("%d", w + i);
	for(int i = 1; i <= m; i ++) scanf("%d%d", s + i, t + i);
	dep[1] = 1, dfs(1);
	for(int i = 1; i <= m; i ++) {
		int &a = s[i], &b = t[i];
		int lca = LCA(a, b);
		oper1[a].push_back({dep[a], 1});
		oper1[lca].push_back({dep[a], -1});
		oper2[b].push_back({dep[a] - dep[lca] * 2, 1});
		if(f[lca][0]) oper2[f[lca][0]].push_back({dep[a] - dep[lca] * 2, -1});
	}
	dfs1(1), dfs2(1);
	for(int i = 1; i <= n; i ++) printf("%d ", ans[i]);
	return 0;
}

标签:NOIP2016,old,int,bucket2,dep,跑步,P1600,bucket1,include
From: https://www.cnblogs.com/azzc/p/16737082.html

相关文章

  • P2822 [NOIP2016 提高组] 组合数问题
    P2822[NOIP2016提高组]组合数问题题解作者岛田小雅这是一道复杂度非常容易爆炸的问题。我看到这题的第一眼,第一反应是直接按照公式暴力跑。我们看一眼数据范围。如......
  • 抖音跑步赚钱
    经常有小伙伴问:抖音没资源怎么变现?我非常理解大家想抓住风口的心情,这几个月我也一直在找那种不要花钱,只要我们持续去做,就一定能赚到钱的方法。在没资源的情况下,淘客算是非......
  • 洛谷P1850 [NOIP2016 提高组] 换教室(期望dp)
    #include<bits/stdc++.h>usingnamespacestd;intn,m,v,e;intc[3005],d[3005];intf[305][305];doubledp[3005][3005][2];//dp[i][j][k]表示前i步申请更换了j......
  • 保姆级跑步姿势详解
    https://www.bilibili.com/video/BV1ER4y1J7hp    这个姿势的错误是迈的步子太大,落地点太过靠前会产生刹车效应正确的跑步姿势落地点,要尽可能靠近身体的正下方......
  • P2058 [NOIP2016 普及组] 海港
    #[NOIP2016普及组]海港##题目背景NOIP2016普及组T3##题目描述小K是一个海港的海关工作人员,每天都有许多船只到达海港,船上通常有很多来自不同国家的乘客。小......
  • P1850 [NOIP2016 提高组] 换教室 思路简记
    我们令\(f_{i,j,0/1}\)表示前\(i\)个时间点,共申请了\(j\)次,第\(i\)个时间点是否(\(1/0\))进行了申请,\(g_{i,j}\)表示\(i\toj\)的最短路,\(p_i\)表示原题中的......