1. 程式人生 > >【洛谷P3038 [USACO11DEC]牧草種植】【樹鏈剖分】【裸】【邊權修改與查詢】

【洛谷P3038 [USACO11DEC]牧草種植】【樹鏈剖分】【裸】【邊權修改與查詢】

【連結】

https://www.luogu.org/problemnew/show/P3038

【題意】

給出一棵n個節點的樹,有m個操作,操作為將一條路徑上的邊權加一或詢問某條邊的權值。

【思路】

樹鏈剖分的裸題。

但是這個題是在邊上進行操作,我們考慮把邊上的操作轉移到點上首先想一下最簡單的鏈的情況。

對於區間[l,r][l,r]的操作會影響r−l+1r−l+1個點,但只會影響r−lr−l條邊,那麼我們可以把每條邊的邊權都放在與它相連的兩個點

深度較深的點上,所以我們每次修改的時候都對(l,r](l,r]進行修改,查詢的時候也如此,具體怎麼實現呢?

只需要在查詢/修改的時候把左區間+1即可

。注意特判一下x==y的情況

【程式碼】

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 6;
const int mod = 1e9 + 7;
vector<int>v[maxn];
int a[maxn];
int deep[maxn];
int fa[maxn];
int tot[maxn];
int top[maxn];
int son[maxn];
int idx[maxn];
int b[maxn];
int cnt = 0;
int root = 1;

struct node {
	int l, r, size, v, lazy;
}t[maxn];

int dfs1(int now, int f, int dep) {
	deep[now] = dep;
	fa[now] = f;
	tot[now] = 1;
	int maxson = -1;
	for (int x : v[now]) {
		if (x == f)continue;
		tot[now] += dfs1(x, now, dep + 1);
		if (tot[x] > maxson) {
			maxson = tot[x];
			son[now] = x;
		}
	}
	return tot[now];
}

void dfs2(int now, int topf) {
	idx[now] = ++cnt;
	a[cnt] = b[now];
	top[now] = topf;
	if (!son[now])return;
	dfs2(son[now], topf);
	for (int nxt : v[now]) {
		if (!idx[nxt]) {
			dfs2(nxt, nxt);
		}
	}
}

void update(int p) {
	t[p].v = (t[p << 1].v + t[p << 1 | 1].v + mod) % mod;
}

void pushdown(int p) {
	if (t[p].lazy) {
		t[p << 1].v = (t[p << 1].v + t[p].lazy*t[p << 1].size) % mod;
		t[p << 1 | 1].v = (t[p << 1 | 1].v + t[p].lazy*t[p << 1 | 1].size) % mod;
		t[p << 1].lazy = (t[p << 1].lazy + t[p].lazy) % mod;
		t[p << 1 | 1].lazy = (t[p << 1 | 1].lazy + t[p].lazy) % mod;
		t[p].lazy = 0;
	}
}

void build(int p, int l, int r) {
	t[p].l = l;
	t[p].r = r;
	t[p].size = r - l + 1;
	if (l == r) {
		t[p].v = a[l];
		return;
	}
	int mid = (l + r) >> 1;
	build(p << 1, l, mid);
	build(p << 1 | 1, mid + 1, r);
	update(p);
}

void intervalAdd(int p, int l, int r, int val) {
	if (l <= t[p].l&&r >= t[p].r) {
		t[p].v += t[p].size*val;
		t[p].lazy += val;
		return;
	}
	pushdown(p);
	int mid = (t[p].l + t[p].r) >> 1;
	if (l <= mid)intervalAdd(p << 1, l, r, val);
	if (r > mid)intervalAdd(p << 1 | 1, l, r, val);
	update(p);
}

int intervalAsk(int p, int l, int r) {
	if (l <= t[p].l&&r >= t[p].r) {
		return t[p].v;
	}
	pushdown(p);
	int mid = (t[p].l + t[p].r) >> 1;
	int ans = 0;
	if (l <= mid)ans += intervalAsk(p << 1, l, r);
	if (r > mid)ans += intervalAsk(p << 1 | 1, l, r);
	return ans;
}

int treeSum(int x, int y) {
	int ans = 0;
	while (top[x] != top[y]) {
		if (deep[top[x]] < deep[top[y]]) swap(x, y);
		ans += intervalAsk(1, idx[top[x]], idx[x]);
		x = fa[top[x]];
	}
	if (deep[x] > deep[y])swap(x, y);
	if(x!=y)ans += intervalAsk(1, idx[x]+1, idx[y]);
	return ans;
}

void treeAdd(int x, int y, int val) {
	while (top[x] != top[y]) {
		if (deep[top[x]] < deep[top[y]])swap(x, y);
		intervalAdd(1, idx[top[x]], idx[x], val);
		x = fa[top[x]];
	}
	if (deep[x] > deep[y])swap(x, y);
	if (x==y)return;
	intervalAdd(1, idx[x]+1, idx[y], val);
}

int main() {
	int n, m;
	scanf("%d%d", &n, &m);
	for (int i = 1; i < n; i++) {
		int x, y;
		scanf("%d%d", &x, &y);
		v[x].push_back(y);
		v[y].push_back(x);
	}
	dfs1(root, 0, 1);
	dfs2(root, root);
	build(1, 1, n);
	while (m--) {
		char op[3];
		int x, y;
		scanf("%s%d%d", op, &x, &y);
		if (op[0] == 'P') {
			treeAdd(x, y, 1);
		}
		else {
			printf("%d\n", treeSum(x,y));
		}
	}
}