您的位置:首页 > 其它

BZOJ 3531 旅行 (树链剖分 + 线段树动态开点)

2018-01-28 13:13 387 查看
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3531

题目大意:中文题就不解释题意了~(-,-)

题目思路:这种树上两点链上查询的题目一眼就是树链剖分了,但是由于是要查询一条链上相同宗教的值,所以就不好用普通的线段树去维护,一开始是想开C棵线段树,最后理智还是战胜了冲动。去查题解,才发现线段树还有动态开点这种操作。

我们正常的线段树某个根节点rt的左儿子的编号是rt<<1,右儿子的编号是rt<<1|1,而动态开线段树的话我们是一边存储一边给左右儿子编号,用root[c]来记录信仰宗教为c的城市的线段树的根节点,每次查询只查询相应点宗教那棵树上的值就行了,记得在修改宗教的时候要把原先那个宗教中的线段树的值变为0,还有就是w和c要一起更新,剩下的就是树链剖分和线段树的常规操作了,具体实现看代码吧。

#include <bits/stdc++.h>
using namespace std;
const int MX = 1e5+7;

struct edge{int v,nxt;}E[MX<<1];
int head[MX],tot;

int n,q,cnt,tmp;
int w[MX],c[MX];
int sz[MX],son[MX],fa[MX],dep[MX],top[MX],id[MX];
int root[MX];//记录不同宗教的线段树的根节点
struct tree{
int sum,MAX;
int ls,rs;
}T[MX*40];//数组记得开大点,因为要调用的空间很多,一开始开小一直RE

void init(){
memset(head,-1,sizeof(head));
tot = cnt = tmp = 0;
}

void add_edge(int u,int v){
E[tot].v = v;E[tot].nxt = head[u];
head[u] = tot++;
E[tot].v = u;E[tot].nxt = head[v];
head[v] = tot++;
}

void dfs1(int u){
sz[u] = 1;son[u] = 0;
for(int i = head[u];~i;i = E[i].nxt){
int v = E[i].v;
if(v == fa[u]) continue;
fa[v] = u;dep[v] = dep[u] + 1;
dfs1(v);
sz[u] += sz[v];
if(sz[v] > sz[son[u]]) son[u] = v;
}
}

void dfs2(int u,int tp){
id[u] = ++cnt; top[u] = tp;
if(son[u]) dfs2(son[u],tp);
for(int i = head[u];~i;i = E[i].nxt){
int v = E[i].v;
if(v == fa[u] || v == son[u]) continue;
dfs2(v,v);
}
}

void push_up(int rt){
T[rt].sum = T[T[rt].ls].sum + T[T[rt].rs].sum;
T[rt].MAX = max(T[T[rt].ls].MAX,T[T[rt].rs].MAX);
}

void update(int p,int x,int l,int r,int &rt){
//rt记得加&,因为是一边存储一边更新结点编号的
if(!rt) rt = ++tmp;
if(l == r){
T[rt].sum = T[rt].MAX = x;
return;
}
int m = (l + r) >> 1;
if(p <= m) update(p,x,l,m,T[rt].ls);
else update(p,x,m+1,r,T[rt].rs);
push_up(rt);
}

int query_sum(int L,int R,int l,int r,int rt){
if(L <= l && r <= R) return T[rt].sum;
int m = (l + r) >> 1;
int res = 0;
if(L <= m) res += query_sum(L,R,l,m,T[rt].ls);
if(R > m) res += query_sum(L,R,m+1,r,T[rt].rs);
return res;
}

int query_max(int L,int R,int l,int r,int rt){
if(L <= l && r <= R) return T[rt].MAX;
int m = (l + r) >> 1;
int res = -1;
if(L <= m) res = max(res,query_max(L,R,l,m,T[rt].ls));
if(R > m) res = max(res,query_max(L,R,m+1,r,T[rt].rs));
return res;
}

int solve(int op,int u,int v,int col){
int ans = 0;
if(!op){
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]]) swap(u,v);
ans = max(ans,query_max(id[top[u]],id[u],1,n,root[col]));
u = fa[top[u]];
}
if(dep[u] > dep[v]) swap(u,v);
ans = max(ans,query_max(id[u],id[v],1,n,root[col]));
} else{
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]]) swap(u,v);
ans += query_sum(id[top[u]],id[u],1,n,root[col]);
u = fa[top[u]];
}
if(dep[u] > dep[v]) swap(u,v);
ans += query_sum(id[u],id[v],1,n,root[col]);
}
return ans;
}

int main(){
//freopen("in.txt","r",stdin);
init();
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++){
int u,v;scanf("%d%d",&u,&v);
add_edge(u,v);
}
dfs1(1);dfs2(1,1);
for(int i = 1;i <= n;i++)
update(id[i],w[i],1,n,root[c[i]]);//每次对c[i]这个颜色的树更新
while(q--){
char op[3];scanf("%s",op);
if(op[0] == 'C'){
if(op[1] == 'C'){
int p,x;scanf("%d%d",&p,&x);
update(id[p],0,1,n,root[c[p]]);//记得先删去原来宗教那棵树上的值
c[p] = x;
update(id[p],w[p],1,n,root[c[p]]);
} else{
int p,x;scanf("%d%d",&p,&x);
update(id[p],x,1,n,root[c[p]]);
w[p] = x;
}
} else{
if(op[1] == 'M'){
int l,r;scanf("%d%d",&l,&r);
//cout<<id[l]<<" "<<id[r]<<endl;
printf("%d\n",solve(0,l,r,c[l]));
} else{
int l,r;scanf("%d%d",&l,&r);
//cout<<id[l]<<" "<<id[r]<<endl;
printf("%d\n",solve(1,l,r,c[l]));
}
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ACM 树链剖分