3306: 树

3306: 树

Time Limit: 10 Sec   Memory Limit: 256 MB
Submit: 715   Solved: 235
[ Submit][ Status][ Discuss]

Description

给定一棵大小为 n 的有根点权树,支持以下操作: 
  • 换根 
  • 修改点权  
     • 查询子树最小值 

Input

  第一行两个整数 n, Q ,分别表示树的大小和操作数。 
  接下来n行,每行两个整数f,v,第i+1行的两个数表示点i的父亲和点i的权。保证f < i。如 果f = 0,那么i为根。输入数据保证只有i = 1时,f = 0。 
  接下来 m 行,为以下格式中的一种: 
  • V x y表示把点x的权改为y 
  • E x 表示把有根树的根改为点 x 
  • Q x 表示查询点 x 的子树最小值 

Output

  对于每个 Q ,输出子树最小值。 

Sample Input


3 7
0 1
1 2
1 3
Q 1
V 1 6
Q 1
V 2 5
Q 1
V 3 4
Q 1


Sample Output



1
2
3
4

HINT

  对于 100% 的数据:n, Q ≤ 10^5。

Source

[ Submit][ Status][ Discuss]

dfs序对应的区间就是子树
这样就能用线段树维护
假设开始时以1为根,称这棵树为T
后来经过不知道多少次操作根已经不是1了
假如在T里面当前询问点是当前根的子树,这样查询不变
假如当前根是当前询问点的祖先,这样询问不变
假如当前根与当前祖先有LCA,这样询问不变
假如当前询问点就是当前根,询问整个T(没考虑到)
假如。。。当前询问点是当前根的父亲,就在这条路径上,当前询问点往下一步,这个点所代表的子树
与当前询问的答案无关,其他都有关
以上结论画个图一目了然

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;

const int maxn = 1E5 + 10;
const int INF = ~0U>>1;

int n,m,clo,root,in[maxn],out[maxn],c[maxn*20],fa[maxn][20],w[maxn],L[maxn];

vector<int> v[maxn];

void Modify(int o,int l,int r,int pos,int modi)
{
	if (l == r) {c[o] = modi; return;}
	int mid = (l+r) >> 1;
	if (pos <= mid) Modify(2*o,l,mid,pos,modi);
	else Modify(2*o+1,mid+1,r,pos,modi);
	c[o] = min(c[2*o],c[2*o+1]);
}

void dfs(int k)
{
	in[k] = ++clo;
	for (int i = 1; i < 20; i++) fa[k][i] = fa[fa[k][i-1]][i-1];
	for (int i = 0; i < v[k].size(); i++) {
		int to = v[k][i];
		L[to] = L[k] + 1; dfs(to);
	}
	Modify(1,1,n,in[k],w[k]);
	out[k] = clo;
}

int query(int o,int l,int r,int ql,int qr)
{
	if (l > r) return INF;
	if (ql <= l && r <= qr) return c[o];
	int mid = (l+r) >> 1,ret = INF;
	if (ql <= mid) ret = min(ret,query(2*o,l,mid,ql,qr));
	if (qr > mid) ret = min(ret,query(2*o+1,mid+1,r,ql,qr));
	return ret;
}

int LCA(int p,int q)
{
	if (L[p] < L[q]) swap(p,q);
	int Log; for (Log = 0; L[p] - (1<<Log) >= 1; Log++); --Log;
	for (int j = Log; j >= 0; j--)
		if (L[p] - (1<<j) >= L[q])
			p = fa[p][j];
	if (p == q) return p;
	for (int j = Log; j >= 0; j--)
		if (fa[p][j] != fa[q][j])
			p = fa[p][j],q = fa[q][j];
	return fa[p][0];
}

int find(int x,int y)
{
	for (int j = 19; j >= 0; j--) 
		if (y - (1 << j) >= 0)
			x = fa[x][j],y -= (1<<j);
	return x;
}

int getcom()
{
	char ch = getchar();
	while (ch != 'V' && ch != 'E' && ch != 'Q') 
		ch = getchar();
	if (ch == 'V') return 1;
	if (ch == 'E') return 2;
	return 3;
}

int main()
{
	#ifdef YZY
		   freopen("yzy.txt","r",stdin);
	#endif
	
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		int x; scanf("%d%d",&x,&w[i]);
		if (!x) root = i; else v[x].push_back(i),fa[i][0] = x;
	}
	L[root] = 1; dfs(root);
	while (m--) {
		int typ = getcom();
		if (typ == 1) {
			int x,y; scanf("%d%d",&x,&y);
			Modify(1,1,n,in[x],y);
		}
		else if (typ == 3) {
			int x; scanf("%d",&x);
			int lca = LCA(root,x);
			if (x == root) printf("%d\n",query(1,1,n,1,n));
			else if (lca == root || (lca != x && lca != root)) 
				printf("%d\n",query(1,1,n,in[x],out[x]));
			else {
				int k;
				if (L[root] - L[x] == 1) k = root;
				else k = find(root,L[root] - L[x] - 1);
				printf("%d\n",min(query(1,1,n,1,in[k]-1),query(1,1,n,out[k]+1,n)));
			}
		}
		else scanf("%d",&root);
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值