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


    rla1rmdq

    题目链接:luogu P6779

    题目大意

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

    思路

    考虑分块。

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

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

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

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

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

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

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

    代码

    #include
    #include
    #include
    #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;
    }
    
  • 相关阅读:
    计算机毕业设计Java智能医疗推荐系统(源码+系统+mysql数据库+Lw文档)
    勒索病毒最新变种.mkp勒索病毒来袭,如何恢复受感染的数据?
    【遍历二叉树的非递归算法,二叉树的层次遍历】
    在BERT与ElasticSearch进行词向量搜索中遇到的各种坑
    Java多线程(7)----浅谈线程池
    2022/11/12 题目练习
    设计模式浅析(六) ·命令模式
    【SpringBoot】YAML 配置文件
    OpenGL 伽马线
    探索H5互动广告:创新数字营销的未来
  • 原文地址:https://blog.csdn.net/weixin_43346722/article/details/127121609