传送门
神烦一下午= =
不过这道题挺好的,让我真正明白了动态开点。和主席树有区别。之前的思路是对的,但是动态开点一直写挂,链剖什么的还是没问题的。
链剖是一眼就能看出来的;需要给每一个宗教建一棵线段树,线段树用动态开点。
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int max_n=1e5+5;
const int max_sz=18;
const int max_e=max_n*2;
const int max_tree=max_n*5;
int n,q,x,y,C,W;
int w[max_n],c[max_n];
char s[5];
int tot,point[max_n],next[max_e],v[max_e];
int size[max_n],father[max_n],son[max_n],h[max_n];
int top[max_n],num[max_n];
int root[max_n],last[max_n];
int N,sz;
struct hp{
int sum,max;
int l,r;
};
hp tree[10*max_n*max_sz];
inline void addedge(int x,int y){
++tot; next[tot]=point[x]; point[x]=tot; v[tot]=y;
++tot; next[tot]=point[y]; point[y]=tot; v[tot]=x;
}
inline void dfs_1(int x,int fa,int dep){
size[x]=1; h[x]=dep; father[x]=fa;
int maxson=0;
for (int i=point[x];i;i=next[i])
if (v[i]!=fa){
dfs_1(v[i],x,dep+1);
size[x]+=size[v[i]];
if (size[v[i]]>maxson){
maxson=size[v[i]];
son[x]=v[i];
}
}
}
inline void dfs_2(int x,int fa){
if (son[fa]!=x) top[x]=x;
else top[x]=top[fa];
num[x]=++N;
if (son[x]) dfs_2(son[x],x);
for (int i=point[x];i;i=next[i])
if (v[i]!=fa&&v[i]!=son[x])
dfs_2(v[i],x);
}
inline void update(int now){
tree[now].sum=tree[ tree[now].l ].sum+tree[ tree[now].r ].sum;
tree[now].max=max(tree[ tree[now].l ].max,tree[ tree[now].r ].max);
}
inline void build(int &now,int l,int r,int x,int v){
int mid=(l+r)>>1;
if (!now) now=++sz;
if (l==r){
tree[now].sum=v;
tree[now].max=v;
return;
}
if (x<=mid)
build(tree[now].l,l,mid,x,v);
else
build(tree[now].r,mid+1,r,x,v);
update(now);
}
inline int query_sum(int now,int l,int r,int lrange,int rrange){
int mid=(l+r)>>1,ans=0;
if (lrange<=l&&r<=rrange)
return tree[now].sum;
if (lrange<=mid)
ans+=query_sum(tree[now].l,l,mid,lrange,rrange);
if (mid+1<=rrange)
ans+=query_sum(tree[now].r,mid+1,r,lrange,rrange);
return ans;
}
inline int query_max(int now,int l,int r,int lrange,int rrange){
int mid=(l+r)>>1,ans=0;
if (lrange<=l&&r<=rrange)
return tree[now].max;
if (lrange<=mid)
ans=max(ans,query_max(tree[now].l,l,mid,lrange,rrange));
if (mid+1<=rrange)
ans=max(ans,query_max(tree[now].r,mid+1,r,lrange,rrange));
return ans;
}
int main(){
// freopen("input.txt","r",stdin);
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;++i){
scanf("%d%d",&x,&y);
addedge(x,y);
}
dfs_1(1,0,1);
dfs_2(1,0);
for (int i=1;i<=n;++i)
build(root[c[i]],1,N,num[i],w[i]);
for (int i=1;i<=q;++i){
scanf("%s",s);
if (s[0]=='C'){
if (s[1]=='C'){
scanf("%d%d",&x,&C);
build(root[c[x]],1,N,num[x],0);
build(root[C],1,N,num[x],w[x]);
c[x]=C;
}
if (s[1]=='W'){
scanf("%d%d",&x,&W);
build(root[c[x]],1,N,num[x],W);
w[x]=W;
}
}
else{
if (s[1]=='S'){
scanf("%d%d",&x,&y);
int st=c[x];
int ans=0;
int f1=top[x],f2=top[y];
while (f1!=f2){
if (h[f1]<h[f2]){
swap(x,y);
swap(f1,f2);
}
ans+=query_sum(root[st],1,N,num[f1],num[x]);
x=father[f1];
f1=top[x];
}
if (num[x]>num[y]) swap(x,y);
ans+=query_sum(root[st],1,N,num[x],num[y]);
printf("%d\n",ans);
}
if (s[1]=='M'){
scanf("%d%d",&x,&y);
int st=c[x];
int ans=0;
int f1=top[x],f2=top[y];
while (f1!=f2){
if (h[f1]<h[f2]){
swap(x,y);
swap(f1,f2);
}
ans=max(ans,query_max(root[st],1,N,num[f1],num[x]));
x=father[f1];
f1=top[x];
}
if (num[x]>num[y]) swap(x,y);
ans=max(ans,query_max(root[st],1,N,num[x],num[y]));
printf("%d\n",ans);
}
}
}
}
动态开点的方法注意一下;每一步想清楚。