3531: [Sdoi2014]旅行

3531: [Sdoi2014]旅行

Time Limit: 20 Sec   Memory Limit: 512 MB
Submit: 1591   Solved: 708
[ Submit][ Status][ Discuss]

Description

 S国有N个城市,编号从1到N。城市间用N-1条双向道路连接,满足
从一个城市出发可以到达其它所有城市。每个城市信仰不同的宗教,如飞天面条神教、隐形独角兽教、绝地教都是常见的信仰。为了方便,我们用不同的正整数代表各种宗教,  S国的居民常常旅行。旅行时他们总会走最短路,并且为了避免麻烦,只在信仰和他们相同的城市留宿。当然旅程的终点也是信仰与他相同的城市。S国政府为每个城市标定了不同的旅行评级,旅行者们常会记下途中(包括起点和终点)留宿过的城市的评级总和或最大值。
    在S国的历史上常会发生以下几种事件:
”CC x c”:城市x的居民全体改信了c教;
”CW x w”:城市x的评级调整为w;
”QS x y”:一位旅行者从城市x出发,到城市y,并记下了途中留宿过的城市的评级总和;
”QM x y”:一位旅行者从城市x出发,到城市y,并记下了途中留宿过
的城市的评级最大值。
    由于年代久远,旅行者记下的数字已经遗失了,但记录开始之前每座城市的信仰与评级,还有事件记录本身是完好的。请根据这些信息,还原旅行者记下的数字。    为了方便,我们认为事件之间的间隔足够长,以致在任意一次旅行中,所有城市的评级和信仰保持不变。

Input

    输入的第一行包含整数N,Q依次表示城市数和事件数。
    接下来N行,第i+l行两个整数Wi,Ci依次表示记录开始之前,城市i的
评级和信仰。
    接下来N-1行每行两个整数x,y表示一条双向道路。
    接下来Q行,每行一个操作,格式如上所述。

Output

    对每个QS和QM事件,输出一行,表示旅行者记下的数字。

Sample Input

5 6
3 1
2 3
1 2
3 3
5 1
1 2
1 3
3 4
3 5
QS 1 5
CC 3 1
QS 1 5
CW 3 3
QS 1 5
QM 2 4

Sample Output

8
9
11
3

HINT

N,Q < =10^5    , C < =10^5


 数据保证对所有QS和QM事件,起点和终点城市的信仰相同;在任意时

刻,城市的评级总是不大于10^4的正整数,且宗教值不大于C。

Source

[ Submit][ Status][ Discuss]



树上修改和询问,很容易想到用LCT解决

但是还有一个关键字是宗教,,总不可能每个节点开n个点维护宗教??

建立n棵树,第i棵树是信仰宗教i的全体子孙的信息,,用LCT维护

这当然是开不下的,,,但是,每次实际需要存的节点信息其实不多

因此,开n棵虚树,用LCT维护虚树,复杂度O(nlogn),,

但是,,常数巨大。。。。。。。。卡常卡了老半天。。。

加了两个小优化过的,,

首先,每个原来的点的信息先储存好,这样建树的时候就直接调用点,不用建完树再重新维护

然后,rotate的时候少个maintain,对于当前splay的节点,留到最后maintain一次就行了


虚树的建立都差点忘光了。。。假设要对n个点建立虚树

把这些点按照dfs序排好,相邻点求lca,如果不在当前集合则新增入集合中

再按dfs排好,维护一个栈,集合内所有元素按顺序入栈

如果栈顶是当前点的祖先,当前点直接入栈

否则当前栈顶不断弹出,每次栈顶与栈顶下面的那个点连边,直到栈顶点是当前点的祖先

最后,剩下的栈内元素一一弹出连边即可

#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<bitset>
#include<algorithm>
#include<cstring>
#include<map>
#include<stack>
#include<set>
#include<cmath>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;

const int maxn = 1E5 + 10;
const int N = 4E5 + 10;

struct Query{
	int typ,x,y; Query(){}
	Query(int typ,int x,int y): typ(typ),x(x),y(y){}
}Q[maxn];
struct data{
	int Num,c; data(){} data(int Num,int c): Num(Num),c(c){}
	bool operator < (const data &b) const {return c < b.c;}
};
struct d2{
	int num,dfn; d2(){} d2(int num,int dfn): num(num),dfn(dfn){}
	bool operator < (const d2 &b) const {return dfn < b.dfn;}
};

int n,m,cnt,tot,dfs_clock,dfn[maxn],c[maxn],vis[maxn],w[maxn],L[maxn]
	,ch[N][2],va[N],fa[N],pfa[N],sum[N],Max[N],rev[N],F[maxn][20],Name[maxn];

vector <int> v[maxn];
vector <int> C[maxn];
vector <d2> s[maxn];
vector <data> g[maxn];
stack <int> S;
stack <d2> s2;

void pushdown(int x)
{
	if (rev[x]) {
		swap(ch[x][0],ch[x][1]);
		for (int i = 0; i < 2; i++) 
			if (ch[x][i]) rev[ch[x][i]] ^= 1;
		rev[x] ^= 1;
	}
}
void maintain(int x)
{
	sum[x] = Max[x] = va[x];
	for (int i = 0; i < 2; i++) {
		sum[x] += sum[ch[x][i]];
		Max[x] = max(Max[x],Max[ch[x][i]]);
	}
}
void rotate(int x)
{
	int y = fa[x],z = fa[y];
	int d = ch[y][0] == x?0:1;
	pfa[x] = pfa[y]; pfa[y] = 0;
	ch[y][d] = ch[x][d^1]; 
	if (ch[y][d]) fa[ch[y][d]] = y;
	ch[x][d^1] = y; fa[y] = x;
	maintain(y); fa[x] = z; 
	if (z) ch[z][ch[z][1] == y] = x;
}
void splay(int u)
{
	for (int z = u; z; z = fa[z]) S.push(z);
	while (!S.empty()) {pushdown(S.top()); S.pop();}
	for (int y = fa[u]; y; rotate(u),y = fa[u])
		if (fa[y])
			rotate((ch[y][0] == u)^(ch[fa[y]][0] == y)?u:y);
	maintain(u);
}
void Access(int x)
{
	for (int z = 0; x; z = x,x = pfa[x]) {
		splay(x);
		if (ch[x][1]) fa[ch[x][1]] = 0,pfa[ch[x][1]] = x;
		if (z) fa[z] = x,pfa[z] = 0;
		ch[x][1] = z; maintain(x);
	}
}
void ChangeRoot(int x) {Access(x); splay(x); rev[x] ^= 1;}
void Join(int x,int y) {ChangeRoot(x); pfa[x] = y; Access(x); splay(x);}

void Dfs(int x,int from)
{
	++cnt; dfn[x] = ++dfs_clock;
	Name[dfs_clock] = x;
	for (int i = 1; i < 18; i++) F[x][i] = F[F[x][i-1]][i-1];
	for (int i = 0; i < C[x].size(); i++) {
		int t = C[x][i];
		if (vis[t] == cnt) continue;
		vis[t] = cnt;
		g[x].push_back(data(++tot,t));
		s[t].push_back(d2(tot,dfn[x]));
		if (t == c[x]) va[tot] = w[x];
	}
	sort(g[x].begin(),g[x].end());
	for (int i = 0; i < v[x].size(); i++) {
		int to = v[x][i];
		if (to == from) continue;
		F[to][0] = x; L[to] = L[x] + 1; Dfs(to,x);
	}
}

int getint()
{
	char ch = getchar();
	int ret = 0;
	while (ch < '0' || '9' < ch) ch = getchar();
	while ('0' <= ch && ch <= '9')
		ret = ret*10 + ch - '0',ch = getchar();
	return ret;
}

char com[20];
int getcom()
{
	scanf("%s",com);
	if (com[1] == 'C') return 1;
	if (com[0] == 'C') return 2;
	if (com[1] == 'S') return 3;
	return 4;
}

void Read()
{
	n = getint(); m = getint();
	for (int i = 1; i <= n; i++) {
		w[i] = getint(); c[i] = getint();
		C[i].push_back(c[i]);
	}
	for (int i = 1; i < n; i++) {
		int x = getint(),y = getint();
		v[x].push_back(y);
		v[y].push_back(x);
	}
	for (int i = 1; i <= m; i++) {
		int typ = getcom();
		int x = getint(),y = getint();
		if (typ == 1) C[x].push_back(y);
		Q[i] = Query(typ,x,y);
	}
}

void Solve()
{
	for (int i = 1; i <= m; i++) {
		int x = Q[i].x,y = Q[i].y;
		int n0 = lower_bound(g[x].begin(),g[x].end(),data(0,c[x])) - g[x].begin();
		data k0 = g[x][n0];
		if (Q[i].typ == 1) {
			int n1 = lower_bound(g[x].begin(),g[x].end(),data(0,y)) - g[x].begin();
			data k1 = g[x][n1];
			ChangeRoot(k0.Num);
			va[k0.Num] = 0; maintain(k0.Num);
			ChangeRoot(k1.Num);
			va[k1.Num] = w[x]; maintain(k1.Num);
			c[x] = y;
		}
		else if (Q[i].typ == 2) {
			ChangeRoot(k0.Num);
			va[k0.Num] = y; maintain(k0.Num);
			w[x] = y;
		}
		else if (Q[i].typ == 3) {
			int ny = lower_bound(g[y].begin(),g[y].end(),data(0,c[y])) - g[y].begin();
			data ky = g[y][ny];
			ChangeRoot(k0.Num); 
			Access(ky.Num); splay(ky.Num);
			int ans = sum[ch[ky.Num][0]];
			printf("%d\n",va[ky.Num] + ans);
		}
		else {
			int ny = lower_bound(g[y].begin(),g[y].end(),data(0,c[y])) - g[y].begin();
			data ky = g[y][ny];
			ChangeRoot(k0.Num); 
			Access(ky.Num); splay(ky.Num);
			int ans = Max[ch[ky.Num][0]];
			printf("%d\n",max(va[ky.Num],ans));
		}
	} 
}

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

int main()
{
	#ifdef DMC
		freopen("DMC.txt","r",stdin);
	#endif
	
	Read(); L[1] = 1; Dfs(1,0);
	for (int i = 1; i <= 100000; i++)
		if (s[i].size() > 1) {
			sort(s[i].begin(),s[i].end());
			int siz = s[i].size(); ++cnt;
			for (int j = 0; j < siz - 1; j++) {
				d2 A = s[i][j],B = s[i][j+1];
				vis[Name[s[i][j].dfn]] = vis[Name[s[i][j+1].dfn]] = cnt;
				int lca = LCA(Name[s[i][j].dfn],Name[s[i][j+1].dfn]);
				if (dfn[lca] != s[i][j].dfn && dfn[lca] != s[i][j+1].dfn && vis[lca] != cnt)
					s[i].push_back(d2(++tot,dfn[lca])),vis[lca] = cnt;
			}
			sort(s[i].begin(),s[i].end()); s2.push(s[i][0]);
			for (int j = 1; j < s[i].size(); j++) {
				d2 A = s[i][j];
				while (!s2.empty()) {
					d2 B = s2.top();
					int lca = LCA(Name[A.dfn],Name[B.dfn]);
					if (dfn[lca] == B.dfn) break;
					s2.pop(); if (!s2.empty()) Join(B.num,s2.top().num);
				}
				s2.push(A);
			}
			for (;;) {
				d2 A = s2.top(); s2.pop();
				if (s2.empty()) break;
				Join(A.num,s2.top().num);
			}
		}
	Solve();
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值