[SDOI2014]旅行
题目描述
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,并记下了途中留宿过的城市的评级最大值。
由于年代久远,旅行者记下的数字已经遗失了,但记录开始之前每座城市的信仰与评级,还有事件记录本身是完好的。请根据这些信息,还原旅行者记下的数字。 为了方便,我们认为事件之间的间隔足够长,以致在任意一次旅行中,所有城市的评级和信仰保持不变。
输入格式
输入的第一行包含整数N,Q依次表示城市数和事件数。
接下来N行,第i+l行两个整数Wi,Ci依次表示记录开始之前,城市i的评级和信仰。 接下来N-1行每行两个整数x,y表示一条双向道路。
接下来Q行,每行一个操作,格式如上所述。
输出格式
对每个QS和QM事件,输出一行,表示旅行者记下的数字。
遇到这种貌似要开n棵线段树,每棵要存n个节点,消耗无数空间的东西时,权值线段树,你值得拥有
这道题就是把正常线段树改成权值线段树就水了
#include<bits/stdc++.h> #define re return #define inc(i,l,r) for(int i=l;i<=r;++i) using namespace std; template<typename T>inline void rd(T&x) { char c;bool f=0; while((c=getchar())<'0'||c>'9')if(c=='-')f=1; x=c^48; while((c=getchar())>='0'&&c<='9')x=x*10+(c^48); if(f)x=-x; } const int maxn=500005; int n,m,tot,k,cnt,hd[maxn]; int T[maxn],Faith[maxn],val[maxn]; int son[maxn],size[maxn],fa[maxn],top[maxn],deep[maxn]; int pos[maxn]; struct node{ int to,nt; }e[maxn*2]; inline void add(int x,int y) { e[++k].to=y;e[k].nt=hd[x];hd[x]=k; e[++k].to=x;e[k].nt=hd[y];hd[y]=k; } //----------------------------------树链剖分-------------------------------------- inline void dfs1(int x) { deep[x]=deep[fa[x]]+1; size[x]=1; if(x==43426) cnt=1; for(int i=hd[x];i;i=e[i].nt) { int v=e[i].to; if(v==fa[x])continue; fa[v]=x; dfs1(v); size[x]+=size[v]; if(size[v]>size[son[x]]) son[x]=v; } } inline void dfs2(int x,int ftop) { top[x]=ftop; pos[x]=++tot; if(son[x]) dfs2(son[x],ftop);//重链剖分 for(int i=hd[x];i;i=e[i].nt) { int v=e[i].to; if(!top[v])dfs2(v,v); } } //--------------------------------------权值线段树--------------------------------------- int ans_sum,ans_max; int sum[maxn<<5],ls[maxn<<5],rs[maxn<<5],maxx[maxn<<5]; //权值线段树要开2*logn inline void pushup(int rt) { sum[rt]=sum[ls[rt]]+sum[rs[rt]]; maxx[rt]=max(maxx[ls[rt]],maxx[rs[rt]]); } inline void add(int &rt,int l,int r,int p,int x) //将p位置的值修改为X { if(!rt)//没有访问过 rt=++cnt; if(l==r) { maxx[rt]=sum[rt]=x;//赋值 re; } int mid=(l+r)>>1; if(p<=mid)add(ls[rt],l,mid,p,x); else add(rs[rt],mid+1,r,p,x); pushup(rt);//总结 } inline void query(int &rt,int l,int r,int x,int y) //查询x~y的值 { if(!rt)re ;//没有被访问过,其值一定为0,不用访问下去,结束 if(x<=l&&r<=y) { ans_sum+=sum[rt]; ans_max=max(maxx[rt],ans_max); re ; } int mid=(l+r)>>1; if(x<=mid)query(ls[rt],l,mid,x,y); if(y>mid) query(rs[rt],mid+1,r,x,y); } int main() { int x,y; rd(n),rd(m); inc(i,1,n) { rd(val[i]);//等级 rd(Faith[i]); //信仰 } inc(i,2,n) { rd(x),rd(y); add(x,y); } dfs1(1); dfs2(1,1); //build()权值线段树版 inc(i,1,n) add(T[Faith[i]],1,maxn,pos[i],val[i]); int cnt=0; char opt[5]; inc(i,1,m) { scanf("%s",opt); rd(x),rd(y); //查询 if(opt[0]=='Q') { ans_sum=ans_max=0; int c=Faith[y]; while(top[x]!=top[y]) { if(deep[top[x]]<deep[top[y]]) swap(x,y); query(T[c],1,maxn,pos[top[x]],pos[x]); x=fa[top[x]]; } if(deep[x]>deep[y])swap(x,y); query(T[c],1,maxn,pos[x],pos[y]); if(opt[1]=='S')printf("%d\n",ans_sum); else printf("%d\n",ans_max); } //修改 else { if(opt[1]=='C') { //改信仰 add(T[Faith[x]],1,maxn,pos[x],0); Faith[x]=y; add(T[Faith[x]],1,maxn,pos[x],val[x]); } else { //改等级 val[x]=y; add(T[Faith[x]],1,maxn,pos[x],val[x]); } } } re 0; }