天天看點

「BZOJ3531」[Sdoi2014]旅行--樹剖好題 3531: [Sdoi2014]旅行

連結:傳送門

  • 3531: [Sdoi2014]旅行

Time Limit: 40 Sec Memory Limit: 512 MB Submit: 3406 Solved: 1528 [Submit][Status][Discuss]

Description

S S S國有 N N N個城市,編号從 1 1 1到 N N N。城市間用 N − 1 N-1 N−1條雙向道路連接配接,滿足

從一個城市出發可以到達其它所有城市。每個城市信仰不同的宗教,如飛天面條神教、隐形獨角獸教、絕地教都是常見的信仰。為了友善,我們用不同的正整數代表各種宗教, S S S國的居民常常旅行。旅行時他們總會走最短路,并且為了避免麻煩,隻在信仰和他們相同的城市留宿。當然旅程的終點也是信仰與他相同的城市。 S S S國政府為每個城市标定了不同的旅行評級,旅行者們常會記下途中(包括起點和終點)留宿過的城市的評級總和或最大值。

在S國的曆史上常會發生以下幾種事件:

” C C   x   c CC\ x\ c CC x c”:城市x的居民全體改信了 c c c教;

” C W   x   w CW\ x\ w CW x w”:城市x的評級調整為 w w w;

” Q S   x   y QS\ x\ y QS x y”:一位旅行者從城市 x x x出發,到城市 y y y,并記下了途中留宿過的城市的評級總和;

” Q M   x   y QM\ x\ y QM x y”:一位旅行者從城市 x x x出發,到城市 y y y,并記下了途中留宿過

的城市的評級最大值。

由于年代久遠,旅行者記下的數字已經遺失了,但記錄開始之前每座城市的信仰與評級,還有事件記錄本身是完好的。請根據這些資訊,還原旅行者記下的數字。 為了友善,我們認為事件之間的間隔足夠長,以緻在任意一次旅行中,所有城市的評級和信仰保持不變。

Input

輸入的第一行包含整數 N , Q N,Q N,Q依次表示城市數和事件數。

接下來N行,第i+l行兩個整數 W i , C i W_i,C_i Wi​,Ci​依次表示記錄開始之前,城市i的

評級和信仰。

接下來 N − 1 N-1 N−1行每行兩個整數 x , y x,y x,y表示一條雙向道路。

接下來 Q Q Q行,每行一個操作,格式如上所述。

Output

對每個 Q S QS QS和 Q M QM 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 &lt; = 1 0 5 , C &lt; = 1 0 5 N,Q &lt; =10^5 , C &lt; =10^5 N,Q<=105,C<=105

資料保證對所有 Q S QS QS和 Q M QM QM事件,起點和終點城市的信仰相同;在任意時

刻,城市的評級總是不大于 1 0 4 10^4 104的正整數,且宗教值不大于 C C C。

Source

R o u n d   1   D a y   1 Round\ 1\ Day\ 1 Round 1 Day 1

題意:

給你一顆無根樹,每個節點代表一座城市,每座城市信仰的宗教 為 C i C_i Ci​,城市評級 W i W_i Wi​ ,實作如下操作:

  • 修改城市 x x x的評級
  • 修改城市 x x x的信仰
  • 查詢從城市 x x x到城市 y y y的最短路徑上的所有信仰宗教 C y C_y Cy​的城市的評級最大值
  • 查詢從城市 x x x到城市 y y y的最短路徑上的所有信仰宗教 C y C_y Cy​的城市的評級之和

題解

  • 1A還是很舒服的233
  • 因為最多1e5種宗教,可以考慮先樹剖出每個節點編号,然後對于每個宗教,用一顆線段樹(動态開點)維護這個宗教所在的所有城市對應的樹剖出來的下标,這樣就可以完成對于亮點之間特定的宗教對應的最大值以及和,複雜度 n log ⁡ n n\log n nlogn(但是如果是要求兩點間所有節點評級和呢?暫時隻知道 n 2 n^2 n2 log ⁡ n \log n logn的做法,即枚舉一下所有兩點之間的城市信仰,然後對每一個信仰再去更新)

附代碼

#include<bits/stdc++.h>

using namespace std;
const int maxn=1e5+10;
const int L=1;
const int R=1e5+5;

vector<int> vec[maxn];
int n,m,u,v,c[maxn],w[maxn];
char opt[20];

// 樹剖用

struct dynamic_segment_tree{
	int roo[maxn],tot; // 為每一個宗教維護一顆動态開點線段樹,每個宗教線上段樹中的位置為其在樹剖時的編号
	struct node{
		int ls,rs,sum,maxx;
	}tree[maxn*20];

	dynamic_segment_tree(){
		tot=0;
		memset(roo,0,sizeof(roo));
		memset(tree,0,sizeof(tree));
	}

	void pushup(int id){
		tree[id].sum=tree[tree[id].ls].sum+tree[tree[id].rs].sum;
		tree[id].maxx=max(tree[tree[id].ls].maxx,tree[tree[id].rs].maxx);
	}
	void update(int l,int r,int k,int val,int id){ //l,r為總區間,k為需要插入的位置,id為目前節點編号
		if(l==r){                                   //插點操作
			tree[id].sum=tree[id].maxx=val;
			return;
		}

		int mid=(l+r)>>1;
		if(k<=mid){
			if(!tree[id].ls) tree[id].ls=++tot;
			update(l,mid,k,val,tree[id].ls);
		}else{
			if(!tree[id].rs) tree[id].rs=++tot;
			update(mid+1,r,k,val,tree[id].rs);
		}
		pushup(id);
	}

	int query_sum(int l,int r,int le,int ri,int id){  //l,r總區間,le,ri待查詢區間,id目前節點編号
		int ans=0;
		if(le<=l&&ri>=r) return tree[id].sum;

		int mid=(l+r)>>1;
		if(le<=mid&&tree[id].ls) ans+=query_sum(l,mid,le,ri,tree[id].ls);
		if(ri>mid&&tree[id].rs) ans+=query_sum(mid+1,r,le,ri,tree[id].rs);
		return ans;
	}

	int query_max(int l,int r,int le,int ri,int id){
		int ans=0;
		if(le<=l&&ri>=r) return tree[id].maxx;

		int mid=(l+r)>>1;
		if(le<=mid&&tree[id].ls) ans=max(ans,query_max(l,mid,le,ri,tree[id].ls));
		if(ri>mid&&tree[id].rs) ans=max(ans,query_max(mid+1,r,le,ri,tree[id].rs));
		return ans;
	}

	void point_delete(int l,int r,int k,int id){
		if(l==r){
			tree[id].maxx=tree[id].sum=0;
			return;
		}
		int mid=(l+r)>>1;
		if(k<=mid) point_delete(l,mid,k,tree[id].ls);
		else point_delete(mid+1,r,k,tree[id].rs);

		pushup(id);
	}

}data;

struct tree{
	int h[maxn],fa[maxn],top[maxn],id[maxn],son[maxn],siz[maxn],rk[maxn],tot;
	tree(){
		tot=0;
		memset(son,0,sizeof(son));
		memset(h,0,sizeof(h));
	}

	void build(){
		dfs1(1,0,1);dfs2(1,1); 
		for(int i=1;i<=n;i++){
			if(!data.roo[c[i]]) data.roo[c[i]]=++data.tot;
			data.update(L,R,id[i],w[i],data.roo[c[i]]);
		}
	}

	void dfs1(int cur,int fath,int he){ //dfs(root,0,1)
 		h[cur]=he;fa[cur]=fath;siz[cur]=1;
 		for(int i=0;i<vec[cur].size();i++){
 			if(vec[cur][i]!=fath){
 				dfs1(vec[cur][i],cur,he+1);
 				siz[cur]+=siz[vec[cur][i]];
 				if(siz[vec[cur][i]]>siz[son[cur]]) son[cur]=vec[cur][i];
 			}
		}
	}


	void dfs2(int cur,int topp){ //dfs2(root,root)
		id[cur]=++tot;rk[tot]=cur;top[cur]=topp;
		if(son[cur]) dfs2(son[cur],topp);
		for(int i=0;i<vec[cur].size();i++){
			if(vec[cur][i]!=fa[cur]&&vec[cur][i]!=son[cur]){
				dfs2(vec[cur][i],vec[cur][i]);
			}
		}
	}

	//區間和
	int query_sum(int x,int y){
		int goal=c[y];
		int topx=top[x],topy=top[y];int ans=0;
		while(topx!=topy){
			if(h[topx]>=h[topy]){
				ans=(ans+data.query_sum(L,R,id[topx],id[x],data.roo[goal]));
				x=fa[topx],topx=top[x];
			}else{
				ans=(ans+data.query_sum(L,R,id[topy],id[y],data.roo[goal]));
				y=fa[topy],topy=top[y];
			}
		}
		ans=(ans+data.query_sum(L,R,min(id[x],id[y]),max(id[x],id[y]),data.roo[goal]));
		return ans;
	}

	//區間最大值
	int query_max(int x,int y){
		int goal=c[y];
		int topx=top[x],topy=top[y];int ans=-1e9;
		while(topx!=topy){
			if(h[topx]>=h[topy]){
				ans=max(ans,data.query_max(L,R,id[topx],id[x],data.roo[goal]));
				x=fa[topx],topx=top[x];
			}else{
				ans=max(ans,data.query_max(L,R,id[topy],id[y],data.roo[goal]));
				y=fa[topy],topy=top[y];
			}
		}
		ans=max(ans,data.query_max(L,R,min(id[x],id[y]),max(id[x],id[y]),data.roo[goal]));
		return ans;
	}

	//單點修改
	void update_c(int x,int val){
		data.point_delete(L,R,id[x],data.roo[c[x]]);
		if(!data.roo[val]) data.roo[val]=++data.tot;
		data.update(L,R,id[x],w[x],data.roo[val]);
		c[x]=val;
	}

	void update_w(int x,int val){
		w[x]=val;
		data.update(L,R,id[x],val,data.roo[c[x]]);
	}

	void debug(){
		printf("son[]: "); for(int i=1;i<=n;i++) printf("%d%c",son[i],i==n?'\n':' ');
		printf("id[]:  ");for(int i=1;i<=n;i++) printf("%d%c",id[i],i==n?'\n':' ');
	}
}chain;


int main()
{
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d %d",&w[i],&c[i]);
	for(int i=1;i<n;i++) {
		scanf("%d %d",&u,&v);
		vec[u].push_back(v);
		vec[v].push_back(u);
	} 

	chain.build();
	for(int i=1;i<=m;i++){
		scanf("%s %d %d",opt+1,&u,&v);
		if(opt[1]=='C'&&opt[2]=='C') chain.update_c(u,v);
		else if(opt[1]=='C'&&opt[2]=='W')  chain.update_w(u,v);
		else if(opt[1]=='Q'&&opt[2]=='S') printf("%d\n",chain.query_sum(u,v));
		else printf("%d\n",chain.query_max(u,v));
	}
	
}
           

繼續閱讀