首页 > 其他分享 >【luogu P6779】rla1rmdq(分块)(树链剖分)

【luogu P6779】rla1rmdq(分块)(树链剖分)

时间:2022-09-30 13:34:40浏览次数:84  
标签:son rt 剖分 分块 int luogu top 树链 now

rla1rmdq

题目链接:luogu P6779

题目大意

给你一个 n 个点的有根树,根给出,和一个值域在 1~n 的数组。
然后 m 次操作,每次对于一个数组的区间 l~r,把它们的值都变成格子树上父亲的编号(如果是已经根就不变),或者求它们到根节点的路径长度的最小值。

思路

考虑分块。

着重看大块的修改和查询。
考虑全局修改和全局查询。

发现因为是全部一起往上蹦,所以如果一个点蹦到了别的点曾经到过的,那它没必要再蹦了。
(永远有它的祖先比它更好)
那所以我们可以暴力蹦,然后暴力删,复杂度是 \(O(n+q)\) 的。

那么考虑到分块上,那我们考虑对于每一大块,算他的贡献。
那我们记录一个答案是整个大块的答案,然后大块修改就像我上面说的暴力改。

接着就是小块,先看修改,会发现一个问题。
就是它可能原本是被删掉的,然后它自己偷偷蹦上去了。
那你就 \(O(1)\) 把它补上就好。

那询问的话,你考虑弄一个 \(tag\) 表示整体加了多少次,再用一个 \(val_i\) 表示数组 \(i\) 这个位置的跳了多少次(如果被删掉了就不会加)
那它的真实位置就是它的 \(tag-val_i\) 级祖先,我们就跳到那里,然后把 \(val_i=tag\) 就可以。
那如果它跳到了一个没有人到过的位置,那它就重新有效。

然后询问你也跳到真实位置然后贡献就可以啦。

然后至于怎么找 \(k\) 级祖先,直接树链剖分搞(轻重链就可以,没必要长链,你分块都已经带 \(\log\) 了)

代码

#include<cmath>
#include<cstdio>
#include<iostream>
#define ll long long 

using namespace std;

const int N = 2e5 + 100;
struct node {
	int x, to, nxt;
}e[N << 1];
int n, m, rt, le[N], KK, B, a[N];
int bl[N], br[N], blo[N], Bn;
struct Quest {
	int op, l, r;
}q[N];
ll ans[N];

void add(int x, int y, int z) {e[++KK] = (node){z, y, le[x]}; le[x] = KK;}

int fa[N], d[N]; ll deg[N]; 
struct SLPF {
	int sz[N], son[N], top[N], dfn[N], rnk[N];
	
	void dfs0(int now, int father) {
		fa[now] = father; sz[now] = 1;
		for (int i = le[now]; i; i = e[i].nxt)
			if (e[i].to != father) {
				deg[e[i].to] = deg[now] + e[i].x;
				d[e[i].to] = d[now] + 1;
				dfs0(e[i].to, now); sz[now] += sz[e[i].to];
				if (sz[e[i].to] > sz[son[now]]) son[now] = e[i].to;
			}
	}
	
	void dfs1(int now, int father) {
		dfn[now] = ++dfn[0]; rnk[dfn[0]] = now;
		if (son[now]) {
			top[son[now]] = top[now]; dfs1(son[now], now);
		}
		for (int i = le[now]; i; i = e[i].nxt)
			if (e[i].to != father && e[i].to != son[now]) {
				top[e[i].to] = e[i].to;
				dfs1(e[i].to, now);
			}
	}
	
	void Init() {
		dfs0(rt, 0);
		top[rt] = rt; dfs1(rt, 0);
	}
	
	int ask_k(int now, int k) {
		if (k >= d[now]) return rt;
		while (d[now] - d[top[now]] < k) {
			k -= d[now] - d[top[now]] + 1;
			now = fa[top[now]];
		}
		return rnk[dfn[now] - k];
	}
}T;

int val[N], sta[N];
bool in[N], met[N];

void slove(int L, int R) {
	for (int i = 1; i <= n; i++)
		val[i] = sta[i] = in[i] = met[i] = 0;
	ll an = 1e18; int tot = 0, tag = 0;
	for (int i = L; i <= R; i++) {
		if (met[a[i]]) continue;
		met[a[i]] = 1; in[i] = 1;
		sta[++tot] = i; an = min(an, deg[a[i]]);
	}
	for (int i = 1; i <= m; i++) {
		int l = max(L, q[i].l), r = min(R, q[i].r);
		if (l > r) continue;
		if (q[i].op == 1) {
			if (L == l && r == R) {
				int tmp = tot; tot = 0; tag++;
				for (int j = 1; j <= tmp; j++) {
					val[sta[j]]++; a[sta[j]] = fa[a[sta[j]]];
					if (met[a[sta[j]]]) in[sta[j]] = 0;
						else {
							met[a[sta[j]]] = 1;
							sta[++tot] = sta[j];
							an = min(an, deg[a[sta[j]]]);
						}
				}
			}
			else {
				for (int j = l; j <= r; j++) {
					a[j] = T.ask_k(a[j], (tag - val[j]) + 1); val[j] = tag;
					if (!met[a[j]]) {
						met[a[j]] = 1;
						if (!in[j]) sta[++tot] = j, in[j] = 1;
						an = min(an, deg[a[j]]);
					}
				}
			}
		}
		if (q[i].op == 2) {
			if (L == l && r == R) ans[i] = min(ans[i], an);
				else {
					for (int j = l; j <= r; j++) {
						a[j] = T.ask_k(a[j], tag - val[j]); val[j] = tag;
						ans[i] = min(ans[i], deg[a[j]]);
					}
				} 
		}
	}
}

int main() {
	scanf("%d %d %d", &n, &m, &rt);
	for (int i = 1; i < n; i++) {
		int x, y, z; scanf("%d %d %d", &x, &y, &z);
		add(x, y, z); add(y, x, z);
	}
	for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
	T.Init();
	
	B = sqrt(n);
	for (int i = 1; i <= n; i++) {
		blo[i] = (i - 1) / B + 1;
		if (!bl[blo[i]]) bl[blo[i]] = i;
		br[blo[i]] = i;
	}
	Bn = blo[n];
	
	for (int i = 1; i <= m; i++) {
		int op, l, r; scanf("%d %d %d", &op, &l, &r);
		q[i] = (Quest){op, l, r}; ans[i] = 1e18;
	}
	for (int i = 1; i <= Bn; i++) slove(bl[i], br[i]);
	
	for (int i = 1; i <= m; i++)
		if (q[i].op == 2) printf("%lld\n", ans[i]);
	
	return 0;
}

标签:son,rt,剖分,分块,int,luogu,top,树链,now
From: https://www.cnblogs.com/Sakura-TJH/p/luogu_P6779.html

相关文章

  • 【luogu CF618G】Combining Slimes(矩阵乘法)(DP)
    CombiningSlimes题目链接:luoguCF618G题目大意有一个长度为n的栈,如果栈顶两个值都是x就会合并成x+1,一开始没有东西。你有p的概率放进去一个1,1-p的概率放入2......
  • luogu P5518 幽灵乐团 / 莫比乌斯反演基础练习题
    重新学习了一下莫比乌斯反演,再来写一次这道题。Part1\[\prod_{i=1}^A\prod_{j=1}^B\prod_{k=1}^C\dfrac{\text{lcm}(i,j)}{\gcd(i,k)}\]\[=\prod_{i=1}^A\prod_{j=1}^B......
  • [luogu p2160] [SHOI2007]书柜的尺寸
    [P2160SHOI2007]书柜的尺寸-洛谷|计算机科学教育新生态(luogu.com.cn)把书按高度从大到小排序,依次考虑放置,每一层第一个被放置的书的高度就是这一层的高度。设\(f......
  • Luogu2607 & Luogu1453 基环树dp
    2607:一个基环树,有点权,全是有向边,选儿子则不能选父亲(反之亦然),问选出的集合的最大点权和注意到题目的特殊性,如果i->hatred[i],那么就是一个内向树,否则为外向树内向树好找环,......
  • luogu P4677 山区建小学
    山区建小学题目描述政府在某山区修建了一条道路,恰好穿越总共\(n\)个村庄的每个村庄一次,没有回路或交叉,任意两个村庄只能通过这条路来往。已知任意两个相邻的村庄之间的距......
  • luogu P1043 [NOIP2003 普及组] 数字游戏
    [NOIP2003普及组]数字游戏题目描述丁丁最近沉迷于一个数字游戏之中。这个游戏看似简单,但丁丁在研究了许多天之后却发觉原来在简单的规则下想要赢得这个游戏并不那么容......
  • luogu P1385 密令
    密令题目描述给定一小写字母串\(s\),每次操作你可以选择一个\(p\)(\(1\leqp\lt|s|\))执行下述修改中的任意一个:将\(s_p\)改为其字典序\(+1\)的字母,将\(s_{p+1}......
  • 【luogu P6419】Kamp(换根DP)
    Kamp题目链接:luoguP6419题目大意一棵树上有一些点有人,边有通过的长度,然后对于每个点,你从这个点出发经过所有人(不用回到原来位置)的最短时间。其它人不会动,只有你去找人......
  • [luogu3980]志愿者招募
    记$x_{i}$为第$i$类志愿者数量$,y_{j}=\sum_{j\in[s_{i},t_{i}]}x_{i}-a_{j}$​,则问题即$$\foralli\in[1,m],x_{i}\ge0\\\forallj\in[1,n],y_{j}\ge0\\y_{1}-\sum_{......
  • 【luogu CF1109E】Sasha and a Very Easy Test(线段树)
    SashaandaVeryEasyTest题目链接:luoguCF1109E题目大意维护一个长度为n的序列,有区间乘,单点除(保证能整除),区间求和答案对p取模。p不一定是质数。思路麻了考场......