zoukankan      html  css  js  c++  java
  • BZOJ3083 遥远的国度 (树链剖分 换根子树求min)

    题意

    传送门

    题解

    除去树剖模板,问题就是换根。写LCT?要维护子树信息比较麻烦。

    树剖仍然可以做。

    我们还是按11为根树剖。对于询问分一下类,假设当前点为uu,根为rtrt

    • u=rtu=rt,直接询问整棵树。
    • rtrt不在uu子树内,则uu在以rtrt为根时的子树和以11为根时的子树相同,直接查询子树。
    • rtrtuu子树内,那么一定可以找到uu的儿子vv使得rtrtvv的子树。那么除去vv子树的所有点都在uu在以rtrt为根时的子树内。dfsdfs序上查询被vv的子树分开的两个区间就行了。

    vv可以倍增,也可以写树剖。树剖见代码。

    CODE

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long LL;
    const int MAXN = 100005;
    const LL INF = 1e12;
    int n, q, rt, fir[MAXN], to[MAXN<<1], nxt[MAXN<<1], cnt, seq[MAXN];
    LL a[MAXN];
    inline void link(int u, int v) {
    	to[++cnt] = v; nxt[cnt] = fir[u]; fir[u] = cnt;
    	to[++cnt] = u; nxt[cnt] = fir[v]; fir[v] = cnt;
    }
    int fa[MAXN], dep[MAXN], son[MAXN], top[MAXN], dfn[MAXN], sz[MAXN];
    int tmr;
    void dfs1(int u, int ff) {
    	dep[u] = dep[fa[u]=ff] + (sz[u]=1);
    	for(int i = fir[u], v; i; i = nxt[i])
    		if((v=to[i]) != ff) {
    			dfs1(v, u), sz[u] += sz[v];
    			if(sz[v] > sz[son[u]]) son[u] = v;
    		}
    }
    void dfs2(int u, int tp) {
    	top[u] = tp;
    	seq[dfn[u] = ++tmr] = u;
    	if(son[u]) dfs2(son[u], tp);
    	for(int i = fir[u], v; i; i = nxt[i])
    		if((v=to[i]) != fa[u] && v != son[u])
    			dfs2(v, v);
    }
    
    LL v[MAXN<<2], lz[MAXN<<2];
    inline void upd(int i) { v[i] = min(v[i<<1], v[i<<1|1]); }
    inline void pushd(int i) {
    	if(~lz[i]) {
    		lz[i<<1] = lz[i<<1|1] = v[i<<1] = v[i<<1|1] = lz[i];
    		lz[i] = -1;
    	}
    }
    void build(int i, int l, int r) {
    	lz[i] = -1;
    	if(l == r) { v[i] = a[seq[l]]; return; }
    	int mid = (l + r) >> 1;
    	build(i<<1, l, mid);
    	build(i<<1|1, mid+1, r);
    	upd(i);
    }
    void modify(int i, int l, int r, int x, int y, LL z) {
    	if(x <= l && r <= y) {
    		lz[i] = v[i] = z; return;
    	}
    	pushd(i);
    	int mid = (l + r) >> 1;
    	if(x <= mid) modify(i<<1, l, mid, x, y, z);
    	if(y > mid) modify(i<<1|1, mid+1, r, x, y, z);
    	upd(i);
    }
    LL query(int i, int l, int r, int x, int y) {
    	if(x <= l && r <= y) return v[i];
    	pushd(i);
    	int mid = (l + r) >> 1; LL re = INF;
    	if(x <= mid) re = min(re, query(i<<1, l, mid, x, y));
    	if(y > mid) re = min(re, query(i<<1|1, mid+1, r, x, y));
    	upd(i); return re;
    }
    void cover(int u, int v, LL w) {
    	while(top[u]^top[v]) {
    		if(dep[top[u]] < dep[top[v]]) swap(u, v);
    		modify(1, 1, n, dfn[top[u]], dfn[u], w); u = fa[top[u]];
    	}
    	if(dep[u] < dep[v]) swap(u, v);
    	modify(1, 1, n, dfn[v], dfn[u], w);
    }
    inline int get(int u, int v) {
    	while(top[u] ^ top[v]) {
    		if(fa[top[v]] == u) return top[v];
    		v = fa[top[v]];
    	}
    	return son[u];
    }
    int main () {
    	scanf("%d%d", &n, &q);
    	for(int i = 1, x, y; i < n; ++i)
    		scanf("%d%d", &x, &y), link(x, y);
    	for(int i = 1; i <= n; ++i) scanf("%lld", &a[i]);
    	dfs1(1, 0), dfs2(1, 1);
    	build(1, 1, n);
    	scanf("%d", &rt);
    	int op, a, b; LL c;
    	while(q--) {
    		scanf("%d%d", &op, &a);
    		if(op == 1) rt = a;
    		else if(op == 2) {
    			scanf("%d%lld", &b, &c);
    			cover(a, b, c);
    		}
    		else {
    			if(rt == a) printf("%lld
    ", v[1]);
    			else if(dfn[rt] < dfn[a] || dfn[rt] >= dfn[a]+sz[a]) printf("%lld
    ", query(1, 1, n, dfn[a], dfn[a]+sz[a]-1));
    			else {
    				int t = get(a, rt); LL ans = INF;
    				ans = min(ans, query(1, 1, n, 1, dfn[t]-1));
    				if(dfn[t]+sz[t] < n)
    				ans = min(ans, query(1, 1, n, dfn[t]+sz[t], n));
    				printf("%lld
    ", ans);
    			}
    		}
    	}
    }
    
    
  • 相关阅读:
    潜入ICU的四逆汤
    经方医的视角
    黄连解毒汤治疗月经过多
    柳暗花明又一方
    PHP 相关性系数计算
    备忘-VSCODE、apache配置
    c# 基于文件系统实现的队列处理类
    notepad++ 快速运行PHP代码
    dat.gui stats.js 通用参数配置及图像统计工具
    AutoHotkey 自动化脚本工具实例
  • 原文地址:https://www.cnblogs.com/Orz-IE/p/12039235.html
Copyright © 2011-2022 走看看