[SDOI2014]旅行
n个城市,n-1条边,任意两个城市互通,每个城市有所信奉的宗教和城市评级,有四种指令:
1.将城市x的居民改信为c教
2.将城市x的评级调整为w
3.统计x到y,路上所有的城市的评级综合(被记录的城市宗教必须与x和y相同,数据保证x和y宗教一致)
4.统计x到y,路上的评级最大值(被记录的城市要求与上述一样)
按照要求输出答案
参考文章为洛谷题解的第一篇(文章好像显示不可查看
洛谷的题解第一页
题目比较麻烦,因为有两个要维护的量,如果是一个还好说,对于树上维护那肯定是树链剖分,现在关键就是如果确保只统计同一宗教的评级
我们可以将每个宗教建立一个线段树这样分开维护不久ok了,但是内存会超,那我们就主席树,动态开点
现在讲讲具体细节:
动态开点就不说了
我们讲讲改信其他教和评级调整如何实现?
原本是a教,改成c教
那我们就先到a教的线段树中找到这个点,然后将其评级和评级最大值都清零(可以理解为删除这个点,但实际还存在只是没有值),然后到c教里将这个点再加上,并加上评级
注意:在删除过程中记得要pushup(root),你删除一个点后,整个数的最大值和权值和发生了改变,所以即可更新到根节点
评级更新也是差不多,先把原本评级删去,再附上新评级
剩下都是树链剖分常规操作
代码有详细注释
#include
using namespace std;
struct node{
int to,next;
}g[1000000];
int tot,n,m,cnt,w[100004],zj[100004],len,head[100004],dep[100004],wson[100004],top[100004],tpos[100004],pre[100004],fa[100004],size[100004];
inline void made(int from,int to){
g[++tot].to=to;
g[tot].next=head[from];
head[from]=tot;
}
inline void dfs1(int rt,int ff){
fa[rt]=ff;
dep[rt]=dep[ff]+1;
size[rt]=1;
for (int i=head[rt];i;i=g[i].next){
int v=g[i].to;
if (v==ff) continue;
dfs1(v,rt);
size[rt]+=size[v];
if (!wson[rt]||size[wson[rt]]<size[v]) wson[rt]=v;
}
}
inline void dfs2(int rt,int tops){
tpos[rt]=++cnt;
pre[cnt]=rt;
top[rt]=tops;
if (wson[rt]) dfs2(wson[rt],tops);
for (int i=head[rt];i;i=g[i].next){
int v=g[i].to;
if (v==fa[rt]||v==wson[rt]) continue;
dfs2(v,v);
}
}
int root[100004];
struct Node{
int l,r,max,tot;
}tree[20000110];
inline void update(int &rt,int w,int l,int r,int pos){
//加点操作
if (!rt) rt=++len;//动态开点
tree[rt].max=max(tree[rt].max,w);
tree[rt].tot+=w;
if (l==r) return;
int mid=(l+r)/2;
if (mid>=pos) update(tree[rt].l,w,l,mid,pos);
else update(tree[rt].r,w,mid+1,r,pos);
}
inline void remove(int &rt,int l,int r,int pos){
//删点操作
if (l==r){
tree[rt].tot=0;
tree[rt].max=0;
return;
}
int mid=(l+r)/2;
if (mid>=pos) remove(tree[rt].l,l,mid,pos);
else remove(tree[rt].r,mid+1,r,pos);
tree[rt].tot=tree[tree[rt].l].tot+tree[tree[rt].r].tot;
tree[rt].max=max(tree[tree[rt].l].max,tree[tree[rt].r].max);
}
inline int querytot(int rt,int lb,int rb,int l,int r){
//查询区间总值
if (r<lb||l>rb) return 0;
if (r>=rb&&l<=lb) return tree[rt].tot;
int mid=(lb+rb)/2;
return querytot(tree[rt].l,lb,mid,l,r)+querytot(tree[rt].r,mid+1,rb,l,r);
}
inline int querymax(int rt,int lb,int rb,int l,int r){
//查询区间最大值
if (r<lb||l>rb) return 0;
if (r>=rb&&l<=lb) return tree[rt].max;
int mid=(lb+rb)/2;
return max(querymax(tree[rt].l,lb,mid,l,r),querymax(tree[rt].r,mid+1,rb,l,r));
}
//--上面为动态开点线段树,下面为树链剖分
inline int sigmax(int u,int v,int zj){
int ans=0;
while (top[u]!=top[v]){
if (dep[top[u]]<dep[top[v]]) swap(u,v);
ans=max(ans,querymax(root[zj],1,n,tpos[top[u]],tpos[u]));
u=fa[top[u]];
}
if (dep[u]<dep[v]) swap(u,v);
ans=max(ans,querymax(root[zj],1,n,tpos[v],tpos[u]));
return ans;
}
inline int sigtot(int u,int v,int zj){
int ans=0;
while (top[u]!=top[v]){
if (dep[top[u]]<dep[top[v]]) swap(u,v);
ans=ans+querytot(root[zj],1,n,tpos[top[u]],tpos[u]);
u=fa[top[u]];
}
if (dep[u]<dep[v]) swap(u,v);
ans=ans+querytot(root[zj],1,n,tpos[v],tpos[u]);
return ans;
}
char s[100];
int main(){
len=0;
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++){
scanf("%d%d",&w[i],&zj[i]);
}
int x,y;
for (int i=1;i<n;i++){
scanf("%d%d",&x,&y);
made(x,y);
made(y,x);
}
dfs1(1,0);
dfs2(1,1);
for (int i=1;i<=n;i++){
update(root[zj[i]],w[i],1,n,tpos[i]);
}
while (m--){
scanf("%s",s);
scanf("%d%d",&x,&y);
switch (s[1]){
case 'C':{
//改信了c教
remove(root[zj[x]],1,n,tpos[x]);
zj[x]=y;
update(root[zj[x]],w[x],1,n,tpos[x]);
break;
}
case 'W':{
//城市x的评级调整为w;
remove(root[zj[x]],1,n,tpos[x]);
w[x]=y;
update(root[zj[x]],w[x],1,n,tpos[x]);
break;
}
case 'S':{
//记录评级总和
printf("%d\n",sigtot(x,y,zj[x]));
break;
}
case 'M':{
//记录评级最大值
printf("%d\n",sigmax(x,y,zj[x]));
break;
}
}
}
return 0;
}