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

被折叠的 条评论
为什么被折叠?



