zoukankan      html  css  js  c++  java
  • 【树链剖分换根】P3979 遥远的国度

    Description

    zcwwzdjn在追杀十分sb的zhx,而zhx逃入了一个遥远的国度。当zcwwzdjn准备进入遥远的国度继续追杀时,守护神RapiD阻拦了zcwwzdjn的去路,他需要zcwwzdjn完成任务后才能进入遥远的国度继续追杀。

    问题是这样的:遥远的国度有n个城市,这些城市之间由一些路连接且这些城市构成了一颗树。这个国度有一个首都,我们可以把这个首都看做整棵树的根,但遥远的国度比较奇怪,首都是随时有可能变为另外一个城市的。遥远的国度的每个城市有一个防御值,有些时候RapiD会使得某两个城市之间的路径上的所有城市的防御值都变为某个值。

    RapiD想知道在某个时候,如果把首都看做整棵树的根的话,那么以某个城市为根的子树的所有城市的防御值最小是多少。

    由于RapiD无法解决这个问题,所以他拦住了zcwwzdjn希望他能帮忙。但zcwwzdjn还要追杀sb的zhx,所以这个重大的问题就被转交到了你的手上。

    Input

    第1行两个整数n m,代表城市个数和操作数。

    第2行至第n行,每行两个整数 u v,代表城市u和城市v之间有一条路。

    第n+1行,有n个整数,代表所有点的初始防御值。

    第n+2行一个整数 id,代表初始的首都为id。

    第n+3行至第n+m+2行,首先有一个整数opt,如果opt=1,接下来有一个整数id,代表把首都修改为id;如果opt=2,接下来有三个整数p1 p2 v,代表将p1 p2路径上的所有城市的防御值修改为v;如果opt=3,接下来有一个整数 id,代表询问以城市id为根的子树中的最小防御值。

    Output

    对于每个opt=3的操作,输出一行代表对应子树的最小点权值。

    Solution

    换根板子题。

    先钦定1是根dfs树剖一波,然后考虑几个操作。

    链修改的操作和树形没关系,直接做。

    考虑查询操作,画图以后可以发现,当一个点(u)不在点(v)的子树中时(原树上),以点(u)为根时(v)的子树形态不变。证明可以考虑此时(u)一定是通过(v)的父亲和(v)连接的,换根后还是通过(v)的父亲,而其他的子树显然还在(u)下方。在此不做展开。

    当点(u)(v)的子树内时,发现(u)(v)是通过指向(u)的儿子链接的。于是(u)的那个儿子儿子所能连接到的所有点(除通过(v)连接的以外)都会在新树上直接作为(u)的子树而不是(v)的子树,剩下的就是(v)的子树了。而那个儿子能链接的所有点恰好是该儿子的子树。于是查询时查询整棵树去掉该儿子子树的min即可。

    另外特判(v)是根的情况。直接查询子树

    Code

    #include <cstdio>
    #include <algorithm>
    #ifdef ONLINE_JUDGE
    #define freopen(a, b, c)
    #endif
    #define rg register
    #define ci const int
    #define cl const long long
    
    typedef long long int ll;
    
    namespace IPT {
    	const int L = 1000000;
    	char buf[L], *front=buf, *end=buf;
    	char GetChar() {
    		if(front == end) {
    			end = buf + fread(front = buf, 1, L, stdin);
    			if(front == end) return -1;
    		}
    		return *(front++);
    	}
    }
    
    template <typename T>
    inline void qr(T &x) {
    	rg char ch = IPT::GetChar(), lst = ' ';
    	while((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar();
    	while((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar();
    	if(lst == '-') x = -x;
    }
    
    template <typename T>
    inline void ReadDb(T &x) {
    	rg char ch = IPT::GetChar(), lst = ' ';
    	while((ch > '9') || (ch < '0')) lst = ch, ch = IPT::GetChar();
    	while((ch >= '0') && (ch <= '9')) x = x * 10 + (ch ^ 48), ch = IPT::GetChar();
    	if(ch == '.') {
    		ch = IPT::GetChar();
    		double base = 1;
    		while((ch >= '0') && (ch <= '9')) x += (ch ^ 48) * ((base *= 0.1)), ch = IPT::GetChar();
    	}
    	if(lst == '-') x = -x;
    }
    
    namespace OPT {
    	char buf[120];
    }
    
    template <typename T>
    inline void qw(T x, const char aft, const bool pt) {
    	if(x < 0) {x = -x, putchar('-');}
    	rg int top=0;
    	do {OPT::buf[++top] = x % 10 + '0';} while( x /= 10);
    	while(top) putchar(OPT::buf[top--]);
    	if(pt) putchar(aft);
    }
    
    const int maxn = 100010;
    const int maxm = 200010;
    
    struct Edge {
    	int to, nxt;
    };
    Edge edge[maxm]; int hd[maxn], ecnt=1;
    inline void cont(ci from, ci to) {
    	Edge &e = edge[++ecnt];
    	e.to = to; e.nxt = hd[from]; hd[from] = ecnt;
    }
    
    int n, m, vistime, newrot;
    int sz[maxn], dfn[maxn], otn[maxn], son[maxn], top[maxn], rmp[maxn], MU[maxn], deepth[maxn], fa[maxn];
    
    const int INF = (1ll << 31) - 1;
    
    struct Tree {
    	Tree *ls, *rs;
    	int v, tag, l, r;
    	
    	inline void pushup() {
    		this->v = INF;
    		if(this->ls) this->v = this->ls->v;
    		if(this->rs) this->v = std::min(this->rs->v, this->v);
    	}
    	
    	inline void maketag(ci _v) {
    		this->v = _v;
    		this->tag = _v;
    	}
    	
    	inline void pushdown() {
    		if(!this->tag) return;
    		if(this->ls) this->ls->maketag(this->tag);
    		if(this->rs) this->rs->maketag(this->tag);
    		this->tag = 0;
    	}
    };
    Tree *pool[maxm],qwq[maxm],*rot;
    int poltp;
    
    void reading();
    void dfs(ci, ci);
    void DFS(ci, ci);
    void buildpool();
    void buildroot();
    void build(Tree*, ci, ci);
    void change(int, int, int);
    void update(Tree*, ci, ci, ci);
    int ask(Tree*, ci, ci);
    
    int main() {
    	freopen("1.in", "r", stdin);
    	qr(n); qr(m);
    	reading();
    	qr(newrot) ;
    	dfs(newrot, 0); DFS(newrot, newrot);
    	buildpool(); buildroot();
    	build(rot, 1, n);
    	int a, b, c, d;
    	while (m--) {
    		a = 0; qr(a);
    		if (a == 1) {
    			newrot = 0; qr(newrot);
    		} else if (a == 2) {
    			b = c = d = 0; qr(b); qr(c); qr(d);
    			change(b, c, d);
    		} else if (a == 3) {
    			a=0; qr(a);
    			if (a == newrot) {
    				qw(rot->v, '
    ', true);
    			} else if ((dfn[newrot] >= dfn[a]) && (dfn[newrot] <= otn[a])) {
    				int tp = newrot;
    				while(deepth[fa[top[tp]]] > deepth[a]) tp = fa[top[tp]];
    				if(dfn[top[tp]] > dfn[a]) tp = top[tp];
    				else tp = rmp[dfn[a] + 1];
    				qw(std::min(ask(rot, 1, dfn[tp] - 1), ask(rot, otn[tp]+1, n)), '
    ', true);
    			} else {
    				qw(ask(rot,dfn[a],otn[a]), '
    ', true);
    			}
    		}
    	}
    	return 0;
    }
    
    void reading() {
    	int a,b;
    	for (rg int i = 1; i < n; ++i) {
    		a = b = 0; qr(a); qr(b);
    		cont(a, b); cont(b, a);
    	}
    	for(rg int i = 1; i <= n; ++i) qr(MU[i]);
    }
    
    void dfs(ci u, ci pree) {
    	sz[u] = 1;
    	deepth[u] = deepth[fa[u] = edge[pree].to] + 1;
    	for (int i = hd[u]; i; i = edge[i].nxt) if (i != pree) {
    		int &to = edge[i].to;
    		dfs(to, i^1);
    		if(sz[to] > sz[son[u]]) son[u] = to;
    	}
    }
    
    void DFS(ci u, ci tp) {
    	if((!u) || (dfn[u])) return;
    	dfn[u] = ++vistime;
    	rmp[vistime] = u;
    	top[u] = tp;
    	DFS(son[u], tp);
    	for (int i = hd[u]; i; i = edge[i].nxt) {
    		int &to = edge[i].to;
    		if(to  == son[u]) continue;
    		DFS(to,to);
    	}
    	otn[u] = vistime;
    }
    
    void buildpool() {
    	for (rg int i = 0; i < maxm; ++i) pool[i] = qwq+i;
    	poltp = maxm - 1;
    }
    
    inline void buildroot() {
    	rot = pool[poltp--];
    }
    
    void build(Tree *u, ci l, ci r) {
    	u->l = l; u->r = r;
    	if(l == r) {
    		u->v = MU[rmp[l]];
    		return;
    	}
    	int mid = (l + r) >> 1;
    	if(l <= mid) {
    		u->ls = pool[poltp--];
    		build(u->ls, l, mid);
    	}
    	if(mid < r) {
    		u->rs = pool[poltp--];
    		build(u->rs, mid+1, r);
    	}
    	u->pushup();
    }
    
    void change(int u, int v, int p) {
    	while(top[u] != top[v]) {
    		if(deepth[top[u]] < deepth[top[v]]) std::swap(u, v);
    		update(rot, dfn[top[u]], dfn[u], p);
    		u = fa[top[u]];
    	}
    	if(deepth[u] < deepth[v]) std::swap(u, v);
    	update(rot, dfn[v], dfn[u], p);
    }
    
    void update(Tree *u, ci l, ci r, ci v) {
    	if((u->l > r) || (u->r < l)) return;
    	if((u->l >= l) && (u->r <= r)) {u->maketag(v); return;}
    	u->pushdown();
    	if(u->ls) update(u->ls, l, r, v);
    	if(u->rs) update(u->rs, l, r, v);
    	u->pushup();
    }
    
    int ask(Tree *u, ci l, ci r) {
    	if((u->l > r) || (u->r < l)) return INF;
    	if((u->l >= l) && (u->r <= r)) return u->v;
    	u->pushdown();
    	int _ret = INF;
    	if(u->ls) _ret = ask(u->ls, l, r);
    	if(u->rs) _ret = std::min(ask(u->rs, l, r), _ret);
    	return _ret;
    }
    

    Summary

    树上的链操作与根无关

    涉及到子树的操作可以通过讨论解决。

  • 相关阅读:
    linux常用服务安装部署
    linux系统基础优化及常用命令
    linux文档和目录结构
    【BZOJ2510】弱题
    【BZOJ3495】PA2010 Riddle
    算法笔记——2-SAT
    路面修整的题解
    三角形的题解
    流星雨的题解
    计算的题解
  • 原文地址:https://www.cnblogs.com/yifusuyi/p/10034761.html
Copyright © 2011-2022 走看看