P3313 [SDOI2014]旅行
分析
老规矩,先来分析分析题目需求
我们总共需要满足以下四个要求
- 将城市x的居民的信仰改为c
- 将城市x的评分全部改为w
- 统计对于旅行者,其从x至y的路径中所有留宿过的城市的评级总和
- 统计对于旅行者,其从x至y的路径中所有留宿过的城市的评级最大值
不难发现,对于每一种信仰而言,我们想要统计对于某一种具体的信仰而言,从x至y的拥有该信仰的城市的评级综合与评级最大值
那最方便的方法,当然是对于每一种信仰直接开一颗线段树就行了....吗?
我们仔细观察后可以发现,我们极限情况,会开\(1e5\)颗线段树,每一颗线段树都是\(1e5\)的范围,那不用想了,必炸。
我们对于该问题的优化,即为动态开点线段树。如果不会,推荐去学习完再来写一下。
那,接下来,这题目就挺板子的了。
细节直接看代码。
Ac_code
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
struct Node
{
int l,r,sum,mx;
}tr[N*20];//开2*Q*log(N)的大小
int h[N],ne[N<<1],e[N<<1],val[N],C[N],idx;
int fa[N],son[N],dep[N],sz[N];
int top[N],id[N],tid[N],ts;
int root[N];//root[N]记录每棵树的根节点编号
int n,q,cnt;//cnt是动态开点的编号
void add(int a,int b)
{
e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
void dfs1(int u,int pa,int depth)
{
sz[u] = 1,dep[u] = depth;
for(int i=h[u];~i;i=ne[i])
{
int j = e[i];
if(j==pa) continue;
fa[j] = u;
dfs1(j,u,depth+1);
sz[u] += sz[j];
if(sz[j]>sz[son[u]]) son[u] = j;
}
}
void dfs2(int u,int tp)
{
top[u] = tp,id[u] = ++ts,tid[ts] = u;
if(!son[u]) return ;
dfs2(son[u],tp);
for(int i=h[u];~i;i=ne[i])
{
int j = e[i];
if(j==fa[u]||j==son[u]) continue;
dfs2(j,j);
}
}
void pushup(int u)
{
tr[u].sum = tr[tr[u].l].sum + tr[tr[u].r].sum;
tr[u].mx = max(tr[tr[u].l].mx,tr[tr[u].r].mx);
}
void modify(int &u,int x,int k,int l,int r)
{
if(!u)//如果没有这个点,那就把这个点开出来,推荐不要搞得很复合,把这个功能独立出来
{
u = ++cnt;
tr[u] = {0};
if(l==r) tr[u].sum = tr[u].mx = val[tid[l]];//如果是叶节点,那就记录一下评级
}
if(l==r) //找到对应的位置
{
tr[u].sum = tr[u].mx = k;
return ;
}
int mid = l + r >> 1;
if(x<=mid) modify(tr[u].l,x,k,l,mid);
else modify(tr[u].r,x,k,mid+1,r);
pushup(u);
}
int querymx(int &u,int ql,int qr,int l,int r)
{
if(!u) return 0;//若此树中没有该区间,则直接返回0
if(ql<=l&&r<=qr) return tr[u].mx;
int mid = l + r >> 1;
int res = 0;
if(ql<=mid) res = max(res,querymx(tr[u].l,ql,qr,l,mid));
if(qr>mid) res = max(res,querymx(tr[u].r,ql,qr,mid+1,r));
pushup(u);
return res;
}
int querysum(int &u,int ql,int qr,int l,int r)
{
if(!u) return 0;//若此树中没有该区间,则直接返回0
if(ql<=l&&r<=qr) return tr[u].sum;
int mid = l + r >> 1;
int res = 0;
if(ql<=mid) res += querysum(tr[u].l,ql,qr,l,mid);
if(qr>mid) res += querysum(tr[u].r,ql,qr,mid+1,r);
pushup(u);
return res;
}
int main()
{
scanf("%d%d",&n,&q);
memset(h,-1,sizeof h);
for(int i=1;i<=n;i++) scanf("%d%d",val+i,C+i);
for(int i=0;i<n-1;i++)
{
int x,y;scanf("%d%d",&x,&y);
add(x,y),add(y,x);
}
dfs1(1,-1,1);
dfs2(1,1);
for(int i=1;i<=n;i++)
modify(root[C[i]],id[i],val[i],1,n);
while(q--)
{
char op[3];
int x,y;
scanf("%s%d%d",op,&x,&y);
if(!strcmp(op,"CC"))
{
modify(root[C[x]],id[x],0,1,n);//将x所在的原树中的该点删除
modify(root[y],id[x],val[x],1,n);//将再将x插入到新的信仰y的线段树中
C[x] = y;//记得更改x的信仰
}
else if(!strcmp(op,"CW"))
{
modify(root[C[x]],id[x],y,1,n);//将x的所在的线段树中,x的评级改变为y
val[x] = y;//记得改变评级
}
else if(!strcmp(op,"QS"))//下边的查询操作就跟普通线段树相同了。
{
int res = 0,c = C[x];
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
res += querysum(root[c],id[top[x]],id[x],1,n);
x = fa[top[x]];
}
if(dep[x]<dep[y]) swap(x,y);
res += querysum(root[c],id[y],id[x],1,n);
printf("%d\n",res);
}
else
{
int res = 0,c = C[x];
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
res = max(res,querymx(root[c],id[top[x]],id[x],1,n));
x = fa[top[x]];
}
if(dep[x]<dep[y]) swap(x,y);
res = max(res,querymx(root[c],id[y],id[x],1,n));
printf("%d\n",res);
}
}
return 0;
}

浙公网安备 33010602011771号