正题
我不喜欢旅行对不起
看到题目,就会知道有三个东西要维护,一个是路径,一个是评级,一个是宗教。
主要是宗教和评级之间的关系,要取路径上宗教为某个特定值的评级最大值或和。
想到用树链剖分来进行重新编号,使得一条重链上的编号连续,然后对于每一个评级,我们开一棵主席树(动态开点线段树无优化)。那么可以节省下来很多空间。
然后树链剖分来进行操作并统计答案即可。
修改宗教和评级相当于就是换根(到另一个宗教的主席树内)和底层信息(向上维护最大值和总和)。
代码<比较简单这题>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
int n,m;
int w[200010],c[200010];
struct edge{
int y,next;
}s[200010];
int first[200010];
int len=0;
int fa[200010],image[200010],fact[200010],dep[200010],tot[200010],son[200010],top[200010];
int mmax[20000010];
int total[20000010];
int ls[20000010],rs[20000010];
int root[200010];
int tx,ty;
int x,y;
int d,v;
int ans,op;
void ins(int x,int y){
len++;
s[len].y=y;s[len].next=first[x];first[x]=len;
}
void dfs_1(int x){
tot[x]=1;
for(int i=first[x];i!=0;i=s[i].next){
int y=s[i].y;
if(y!=fa[x]){
dep[y]=dep[x]+1;
fa[y]=x;
dfs_1(y);
if(tot[son[x]]<tot[y]) son[x]=y;
tot[x]+=tot[y];
}
}
}
void dfs_2(int x,int tp){
top[x]=tp;image[x]=++len;fact[len]=x;
if(son[x]!=0) dfs_2(son[x],tp);
for(int i=first[x];i!=0;i=s[i].next){
int y=s[i].y;
if(y!=fa[x] && y!=son[x])
dfs_2(y,y);
}
}
void update(int &now,int l,int r){
if(now==0) now=++len;
total[now]+=d;
if(l==r) {
if(d>0) mmax[now]=d;
else mmax[now]=0;
return ;
}
if(v<=(l+r)/2) update(ls[now],l,(l+r)/2);
else update(rs[now],(l+r)/2+1,r);
mmax[now]=max(mmax[ls[now]],mmax[rs[now]]);
}
void change_c(){
scanf("%d %d",&x,&y);
v=image[x];d=-w[x];
update(root[c[x]],1,n);
c[x]=y;d=w[x];
update(root[c[x]],1,n);
}
void change_w(){
scanf("%d %d",&x,&y);
v=image[x];d=-w[x];
update(root[c[x]],1,n);
w[x]=y;d=w[x];
update(root[c[x]],1,n);
}
int query_sum(int now,int l,int r,int x,int y){
if(x==l && y==r) return total[now];
if(r<=(x+y)/2) return query_sum(ls[now],l,r,x,(x+y)/2);
else if((x+y)/2<l) return query_sum(rs[now],l,r,(x+y)/2+1,y);
else return query_sum(ls[now],l,(x+y)/2,x,(x+y)/2)+query_sum(rs[now],(x+y)/2+1,r,(x+y)/2+1,y);
}
int get_sum(){
scanf("%d %d",&x,&y);
op=x;
tx=top[x],ty=top[y];
ans=0;
while(tx!=ty){
if(dep[tx]>dep[ty]){
swap(x,y);swap(tx,ty);
}
ans+=query_sum(root[c[op]],image[ty],image[y],1,n);
y=fa[ty];ty=top[y];
}
if(dep[x]>dep[y]) swap(x,y);
ans+=query_sum(root[c[op]],image[x],image[y],1,n);
return ans;
}
int query_max(int now,int l,int r,int x,int y){
if(x==l && y==r) return mmax[now];
if(r<=(x+y)/2) return query_max(ls[now],l,r,x,(x+y)/2);
else if((x+y)/2<l) return query_max(rs[now],l,r,(x+y)/2+1,y);
else return max(query_max(ls[now],l,(x+y)/2,x,(x+y)/2),query_max(rs[now],(x+y)/2+1,r,(x+y)/2+1,y));
}
int get_max(){
scanf("%d %d",&x,&y);
op=x;
tx=top[x],ty=top[y];
ans=0;
while(tx!=ty){
if(dep[tx]>dep[ty]){
swap(x,y);swap(tx,ty);
}
ans=max(ans,query_max(root[c[op]],image[ty],image[y],1,n));
y=fa[ty];ty=top[y];
}
if(dep[x]>dep[y]) swap(x,y);
ans=max(ans,query_max(root[c[op]],image[x],image[y],1,n));
return ans;
}
int main(){
scanf("%d %d",&n,&m);
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);
ins(x,y);
ins(y,x);
}
dep[1]=1;tot[1]=0;son[1]=0;fa[1]=0;dfs_1(1);
len=0;dfs_2(1,1);
len=0;
for(int i=1;i<=n;i++){
v=image[i];d=w[i];
update(root[c[i]],1,n);
}
char ch[10];
while(m--){
scanf("%s",ch);
if(ch[1]=='C') change_c();
else if(ch[1]=='W') change_w();
else if(ch[1]=='S') printf("%d\n",get_sum());
else if(ch[1]=='M') printf("%d\n",get_max());
}
}
本文介绍了一种利用树链剖分和主席树解决特定路径查询问题的方法。通过重新编号节点使重链上的编号连续,并使用动态开点线段树来维护每个评级的最大值或和。适用于维护宗教和评级之间的关系。
2774

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



