【bzoj3513】【SDOI2014】【旅行】【树链剖分+动态开点】

本文介绍了一个关于旅行者在不同信仰城市间旅行的问题,并通过建立线段树来解决旅行过程中遇到的多种查询及更新操作。文章详细解释了如何通过动态开点的方法优化内存使用。

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。

题解:对每种颜色建线段树。然后就是基本操作了。

            普通建肯定MLE。所以我们再使用动态开点就好了。

            所谓动态开点,其实就是每次最多只新建一条你需要的链。这样空间复杂度就成了q*log(n);           

代码:

#include<iostream>
#include<cstdio>
#define N 100010
using namespace std;
struct tree{int mx,s;}t[N*100];
struct use{int st,en;}e[N*2];
int r[N],deep[N],fa[N][20],size[N],cnt,bl[N],sz,pos[N],ans,tot;
int n,m,w[N],c[N],q,point[N],next[N*2],x,y,ls[N*100],rs[N*100],tt; 
char ch[10]; 
void add(int x,int y){next[++cnt]=point[x];point[x]=cnt;e[cnt].en=y;}
void dfs(int x){
  size[x]=1;
  for (int i=1;i<=18;i++){
  	if ((1<<i)<=deep[x])fa[x][i]=fa[fa[x][i-1]][i-1]; else break;
  }
  for (int i=point[x];i;i=next[i])
    if (e[i].en!=fa[x][0]){
      fa[e[i].en][0]=x;deep[e[i].en]=deep[x]+1;
      dfs(e[i].en);size[x]+=size[e[i].en];
    }
}
void get(int x,int c){
	pos[x]=++sz;bl[x]=c;int k(0);
	for (int i=point[x];i;i=next[i]){
		if (e[i].en!=fa[x][0]&&size[k]<size[e[i].en]) k=e[i].en;
	} 
	if (k==0) return;get(k,c);
	for (int i=point[x];i;i=next[i]){
		if (e[i].en!=k&&e[i].en!=fa[x][0]) get(e[i].en,e[i].en);
	} 
}
int lca(int x,int y){
   if (deep[x]<deep[y]) swap(x,y);int d=deep[x]-deep[y];
   for (int i=0;i<=18;i++) if (d&(1<<i)) x=fa[x][i];
   for (int i=18;i>=0;i--) if (fa[x][i]!=fa[y][i]){x=fa[x][i];y=fa[y][i];}
   if (x==y) return x;else return fa[x][0];
} 
void change(int &k,int l,int r,int x,int v){
  if (!k) k=++tot;int mid=(l+r)>>1;
  if (l==r){t[k].mx=v;t[k].s=v;return;}
  if (x<=mid) change(ls[k],l,mid,x,v);else change(rs[k],mid+1,r,x,v);
  t[k].mx=max(t[ls[k]].mx,t[rs[k]].mx);t[k].s=t[ls[k]].s+t[rs[k]].s;
}
int qx(int k,int l,int r,int ll,int rr){	
	if (!k) return 0;int mid=(l+r)>>1,ans(0);
	if (ll<=l&&r<=rr) return t[k].mx;
	if(ll<=mid) ans=max(ans,qx(ls[k],l,mid,ll,rr));
	if (rr>mid) ans=max(ans,qx(rs[k],mid+1,r,ll,rr)); 
	return ans;
}
int qs(int k,int l,int r,int ll,int rr){	
  if (!k) return 0;int mid=(l+r)>>1,ans(0);
  if (ll<=l&&r<=rr) return t[k].s;
  if(ll<=mid) ans+=qs(ls[k],l,mid,ll,rr);
  if (rr>mid) ans+=qs(rs[k],mid+1,r,ll,rr); 
  return ans;
} 
int qmax(int k,int x,int y){
  int ans(0);
  while (bl[x]!=bl[y]){
   ans=max(ans,qx(r[k],1,n,pos[bl[x]],pos[x]));x=fa[bl[x]][0];
  }
  ans=max(ans,qx(r[k],1,n,pos[y],pos[x]));return ans;
}
int qsum(int k,int x,int y){
  int ans(0);
  while (bl[x]!=bl[y]){
   ans+=qs(r[k],1,n,pos[bl[x]],pos[x]);x=fa[bl[x]][0];
  } 
  ans+=qs(r[k],1,n,pos[y],pos[x]);return ans;
}
int main(){
  scanf("%d%d",&n,&q);
  for (int i=1;i<=n;i++) scanf("%d%d",&w[i],&c[i]);
  for (int i=1;i<=n-1;i++){scanf("%d%d",&x,&y);add(x,y);add(y,x);}
  dfs(1);get(1,1);for (int i=1;i<=n;i++) change(r[c[i]],1,n,pos[i],w[i]);	
  for (int i=1;i<=q;i++){
  	scanf("%s",ch);scanf("%d%d",&x,&y);
  	if (ch[1]=='C'){
      change(r[c[x]],1,n,pos[x],0);
	  c[x]=y;change(r[c[x]],1,n,pos[x],w[x]);
	}
    if (ch[1]=='W') {change(r[c[x]],1,n,pos[x],y);w[x]=y;}
    if (ch[1]=='S'){
	 tt=lca(x,y);ans=qsum(c[x],x,tt)+qsum(c[y],y,tt);
	 if (c[x]==c[tt]) ans-=w[tt];printf("%d\n",ans);
	}
	if (ch[1]=='M'){
	 tt=lca(x,y);printf("%d\n",max(qmax(c[x],x,tt),qmax(c[y],y,tt)));
    }
  }
}


           

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值