题目链接:https://www.luogu.com.cn/problem/P3313
题目描述
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事件,输出一行,表示旅行者记下的数字。
输入输出样例
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
8 9 11 3
说明/提示
N,Q < =10^5 , C < =10^5
数据保证对所有QS和QM事件,起点和终点城市的信仰相同;在任意时
刻,城市的评级总是不大于10^4的正整数,且宗教值不大于C。
一眼树剖,然后就不会了。。。难点在于只计算经过的相同信仰的城市,这个处理好了就是个裸的树剖了。
至于怎么处理,我们可以对每种信仰建一棵树,但由于信仰的范围很大,所以为了避免MLE,我们必须动态开点,动态分配所需要的内存。
于是乎,这题好像也就结束了。。。
对每个信仰建树:
for (int i=1; i<=n; i++) update(1,n,a[i].w,id[i],root[a[i].c]);
即信仰为a[i].c的树根编号存储在root中,到时候树剖查询的时候我们只需要从信仰为c的树,其根为root[c]的树中开始找就可以了。
至于update函数,就是普通的动态开点过程:
inline void pushup(int rt) { tree[rt].sum=tree[tree[rt].l].sum+tree[tree[rt].r].sum; tree[rt].maxx=max(tree[tree[rt].l].maxx,tree[tree[rt].r].maxx); } inline void update(int l,int r,int val,int pos,int &rt) { if (!rt) rt=++pot;//动态开点 if (l==r){ tree[rt].sum=tree[rt].maxx=val; return; } int mid=(l+r)>>1; if (mid>=pos) update(l,mid,val,pos,tree[rt].l); else update(mid+1,r,val,pos,tree[rt].r); pushup(rt); }
然后走一波树剖准备工作,重儿子,节点深度,top数组之类的准备完毕,接下来就是跑操作了,更改级别是没什么难度,就是直接调用update函数就OK了:
update(1,n,val,id[x],root[a[x].c]);
更改信仰也是一样的操作,不过它需要update两次,第一次将原来的信仰树中的值删除,即赋值为0,第二次在现在的信仰树中更新:
update(1,n,0,id[pos],root[a[pos].c]);
a[pos]=node{w,c};
update(1,n,a[pos].w,id[pos],root[a[pos].c]);
更新操作就这样了,不是很难。
接下来就是跑树剖了,照着板子敲一遍就完事了:
inline int query_range(int l,int r,int flag) { int lv=a[l].c,s=0; while (top[l]!=top[r]){ if (deep[top[l]]<deep[top[r]]) swap(l,r); if (!flag) s+=query(1,n,id[top[l]],id[l],root[lv],0); else s=max(s,query(1,n,id[top[l]],id[l],root[lv],1)); l=fa[top[l]]; } if (deep[l]>deep[r]) swap(l,r); if (!flag) s+=query(1,n,id[l],id[r],root[lv],0); else s=max(s,query(1,n,id[l],id[r],root[lv],1)); return s; }
至于query函数的话。。。学了线段树的都会写吧,就不贴了。
以下是AC代码:
#include <bits/stdc++.h> using namespace std; const int mac=1e5+10; struct node { int w,c; }a[mac]; struct Edge { int to,next; }eg[mac<<1]; struct Tree { int l,r,sum,maxx; }tree[mac*30]; int num=0,head[mac],deep[mac],top[mac],id[mac],n; int son[mac],dson[mac],fa[mac],use=0,root[mac],pot=0; inline void in(int &x) { char ch=getchar(); int f=0; while (ch>'9' || ch<'0') ch=getchar(); while (ch<='9' && ch>='0') f=(f<<1)+(f<<3)+ch-'0',ch=getchar(); x=f; } inline void add(int u,int v) { eg[++num]=Edge{v,head[u]}; head[u]=num; } inline void dfs1(int x,int f) { son[x]=1;fa[x]=f; int maxson=-1; for (int i=head[x]; i!=-1; i=eg[i].next){ int v=eg[i].to; if (v==f) continue; deep[v]=deep[x]+1; dfs1(v,x); son[x]+=son[v]; if (son[v]>maxson) dson[x]=v,maxson=son[v]; } } inline void dfs2(int x,int tp) { id[x]=++use;top[x]=tp; if (!dson[x]) return; dfs2(dson[x],tp); for (int i=head[x]; i!=-1; i=eg[i].next){ int v=eg[i].to; if (v==fa[x] || v==dson[x]) continue; dfs2(v,v); } } inline void pushup(int rt) { tree[rt].sum=tree[tree[rt].l].sum+tree[tree[rt].r].sum; tree[rt].maxx=max(tree[tree[rt].l].maxx,tree[tree[rt].r].maxx); } inline void update(int l,int r,int val,int pos,int &rt) { if (!rt) rt=++pot; if (l==r){ tree[rt].sum=tree[rt].maxx=val; return; } int mid=(l+r)>>1; if (mid>=pos) update(l,mid,val,pos,tree[rt].l); else update(mid+1,r,val,pos,tree[rt].r); pushup(rt); } inline void update_city(int pos,int c,int w,int flag) { if (flag) update(1,n,0,id[pos],root[a[pos].c]); a[pos]=node{w,c}; update(1,n,a[pos].w,id[pos],root[a[pos].c]); } inline int query(int l,int r,int L,int R,int rt,int flag) { int ans=0; if (l>=L && r<=R){ if (!flag) return tree[rt].sum; else return tree[rt].maxx; } int mid=(l+r)>>1; if (mid>=L) { if (!flag) ans+=query(l,mid,L,R,tree[rt].l,flag); else ans=max(ans,query(l,mid,L,R,tree[rt].l,flag)); } if (mid<R){ if (!flag) ans+=query(mid+1,r,L,R,tree[rt].r,flag); else ans=max(ans,query(mid+1,r,L,R,tree[rt].r,flag)); } return ans; } inline int query_range(int l,int r,int flag) { int lv=a[l].c,s=0; while (top[l]!=top[r]){ if (deep[top[l]]<deep[top[r]]) swap(l,r); if (!flag) s+=query(1,n,id[top[l]],id[l],root[lv],0); else s=max(s,query(1,n,id[top[l]],id[l],root[lv],1)); l=fa[top[l]]; } if (deep[l]>deep[r]) swap(l,r); if (!flag) s+=query(1,n,id[l],id[r],root[lv],0); else s=max(s,query(1,n,id[l],id[r],root[lv],1)); return s; } int main() { //freopen("in.txt","r",stdin); int q; in(n);in(q); memset(head,-1,sizeof head); for (int i=1; i<=n; i++){ int w,c; in(w);in(c); a[i]=node{w,c}; } for (int i=1; i<n; i++){ int u,v; in(u);in(v); add(u,v);add(v,u); } deep[1]=id[1]=top[1]=1; dfs1(1,0); dfs2(1,1); for (int i=1; i<=n; i++) update(1,n,a[i].w,id[i],root[a[i].c]); while (q--){ char s[5]; int l,r,x,w,c; scanf ("%s",s); if (s[1]=='C'){ in(x);in(c); update_city(x,c,a[x].w,1); } else if (s[1]=='W'){ in(x);in(w); update_city(x,a[x].c,w,0); } else if (s[1]=='S'){ in(l);in(r); int ans=query_range(l,r,0); printf ("%d\n",ans); } else{ in(l);in(r); int ans=query_range(l,r,1); printf ("%d\n",ans); } } return 0; }