P3258 [JLOI2014]松鼠的新家

传送门

先说一下做题的经历吧:因为昨天晚上刚打了比赛,今天又没有事干,然后看到自己的收藏里还有一道树剖就去莽了,然后就1A了(头一次1A紫题有点*激动>_<,然后就来写题解了)。

题目大意

给出****走的路线图,他走过的每一个点上都要放一个糖果.(显然就是一个树剖啊,为什么要差分,我又不会,只能写写树剖*持一下生活。)

思路:

一开始我以为是纯纯的树剖,结果看了一下样例发现不对。然后发现题目是说走出这个节点就不算再在走出的这个节点放糖了,还有最后餐厅不用放糖。

那么我们就可以先把走出的那个点的糖果数减去1, 然后就可以 直接套树剖了,但是我们这样做就有一个弊端,那就是第一个点,也就是开始的节点处一定会少一颗糖,
我们可以记录下来开始点,然后最后查询的时候给他加上一颗糖就好了。

code:

#include <bits/stdc++.h>

#define N 300010
#define M 1010

using namespace std;
int n;
int siz[N], top[N], dfn[N], son[N];//树剖常用数组。
int pre[N], dep[N], fa[N], w[N], lux[N];//lux来记录走过的路线。
struct node {
	int next, to;
}edge[N << 1];

int read() {
	int s = 0, f = 0; char ch = getchar();
	while (!isdigit(ch)) f |= (ch == '-'), ch = getchar();
	while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
	return f ? -s : s;
}

namespace Seg {//线段树。
	#define lson rt << 1
	#define rson rt << 1 | 1
	struct node {
		int sum, len, lazy;
	}tree[N << 2];
	void push_up(int rt) {
		tree[rt].sum = tree[lson].sum + tree[rson].sum;
	}
	void build(int rt, int l, int r) {
		tree[rt].len = r - l + 1;
		if (l == r) {
			tree[rt].sum = w[pre[l]];
			return;
		}
		int mid = (l + r) >> 1;
		build(lson, l, mid);
		build(rson, mid + 1, r);
		push_up(rt);
	}
	void push_down(int rt) {
		if (!tree[rt].lazy) return;
		tree[lson].sum += tree[rt].lazy * tree[lson].len;
		tree[rson].sum += tree[rt].lazy * tree[rson].len;
		tree[lson].lazy += tree[rt].lazy;
		tree[rson].lazy += tree[rt].lazy;
		tree[rt].lazy = 0;
	}
	void update(int rt, int c, int l, int r, int L, int R) {
		if (L <= l && r <= R) {
			tree[rt].lazy += c;
			tree[rt].sum += tree[rt].len * c;
			return;
		}
		push_down(rt);
		int mid = (l + r) >> 1;
		if (L <= mid) update(lson, c, l, mid, L, R);
		if (R > mid) update(rson, c, mid + 1, r, L, R);
		push_up(rt);
	}
	int query(int rt, int l, int r, int pos) {
		if (l == r) return tree[rt].sum;
		push_down(rt);
		int mid = (l + r) >> 1, ans = 0;
		if (pos <= mid) ans += query(lson, l, mid, pos);
		if (pos > mid) ans += query(rson, mid + 1, r, pos);
		return ans;
	}
}

namespace Cut {
	int head[N << 1], add_edge, cnt;
	void add(int from, int to) {
		edge[++add_edge].next = head[from];
		edge[add_edge].to = to;
		head[from] = add_edge;
	}
	void dfs(int x, int fath) {
		fa[x] = fath, siz[x] = 1, dep[x] = dep[fath] + 1; 
		for (int i = head[x]; i; i = edge[i].next) {
			int to = edge[i].to;
			if (to == fath) continue;
			dfs(to, x);
			siz[x] += siz[to];
			if (siz[son[x]] < siz[to]) son[x] = to;
		}
	}
	void dfs2(int x, int tp) {
		dfn[x] = ++cnt, pre[cnt] = x, top[x] = tp;
		if (son[x]) dfs2(son[x], tp);
		for (int i = head[x]; i; i = edge[i].next) {
			int to = edge[i].to;
			if (to == son[x] || fa[x] == to) continue;
			dfs2(to, to);
		} 
	}
	void change(int x, int y, int c) {
		Seg::update(1, -c, 1, n, dfn[x], dfn[x]);//先把开始点减去一。
		while (top[x] != top[y]) {//然后再搞一下裸地树剖。
			if (dep[top[x]] < dep[top[y]]) swap(x, y);
			Seg::update(1, c, 1, n, dfn[top[x]], dfn[x]);
			x = fa[top[x]];
		}
		if (dep[x] > dep[y]) swap(x, y);
		Seg::update(1, c, 1, n, dfn[x], dfn[y]);
	}
}

int main() {
	n = read();
	for (int i = 1; i <= n; i++) lux[i] = read();
	for (int i = 1, x, y; i <= n - 1; i++) {
		x = read(), y = read();
		Cut::add(x, y), Cut::add(y, x);
	}
	Cut::dfs(1, 0), Cut::dfs2(1, 1);Seg::build(1, 1, n);
	for (int i = 1; i <= n - 1; i++) {
		int x = lux[i], y = lux[i + 1];
		Cut::change(x, y, 1);
	}
	int lst = lux[n], fri = lux[1];//记录一下起始点和最后的餐厅。
	for (int i = 1; i <= n; i++) {
		int s = Seg::query(1, 1, n, dfn[i]);
		if (i == fri) printf("%d\n", s + 1);//起始点糖果数+1
		else if (i == lst) printf("%d\n", s - 1);//餐厅糖果数-1
		else printf("%d\n", s);
	}
	return 0;
}
posted @ 2019-12-22 11:03  Kersen  阅读(167)  评论(0编辑  收藏  举报