P3313 [SDOI2014]旅行

P3313 [SDOI2014]旅行

分析

老规矩,先来分析分析题目需求

我们总共需要满足以下四个要求

  1. 将城市x的居民的信仰改为c
  2. 将城市x的评分全部改为w
  3. 统计对于旅行者,其从x至y的路径中所有留宿过的城市的评级总和
  4. 统计对于旅行者,其从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;
}
posted @ 2022-04-30 23:20  艾特玖  阅读(47)  评论(0)    收藏  举报