BZOJ3531-[Sdoi2014]旅行


题解:
正常的树链剖分,但由于颜色的限制,不能只开一棵线段树,我们可以对每个颜色都开一棵线段树,但这样空间不行,我们只要建实时开点线段树,就可以了。
对于询问,只要在那个颜色的线段树上询问就好了。
Code C o d e :

#include<iostream>
#include<cstdio>
#include<cstring>
#define M 10000005
#define N 100005
using namespace std;
int n,m,cnt,place,size;
int s[17],w[N],c[N],root[N],fa[N][17],deep[N],vis[N];
int pl[N],belong[N],son[N],head[N],ls[M],rs[M],mx[M],sum[M];
struct data
{
    int vet,next;
}edge[N*2];
void ins(int u,int v)
{
    edge[++cnt].vet=v;
    edge[cnt].next=head[u];
    head[u]=cnt;
}
void dfs1(int x)
{
    vis[x]=1;son[x]=1;
    for(int i=1;i<=16;i++)
        if(s[i]<=deep[x])fa[x][i]=fa[fa[x][i-1]][i-1];
            else break;
    for(int i=head[x];i;i=edge[i].next)
    {
        int v=edge[i].vet;
        if(vis[v])continue;
        deep[v]=deep[x]+1;
        fa[v][0]=x;
        dfs1(v);
        son[x]+=son[v];
    }
}
void dfs2(int x,int chain)
{
    place++;
    pl[x]=place;
    belong[x]=chain;
    int k=0;
    for(int i=head[x];i;i=edge[i].next)
    {
        int v=edge[i].vet;
        if(deep[v]>deep[x]&&son[v]>son[k])
            k=v;
    }
    if(k)dfs2(k,chain);
    for(int i=head[x];i;i=edge[i].next)
    {
        int v=edge[i].vet;
        if(deep[v]>deep[x]&&v!=k)
            dfs2(v,v); 
    }
}
int lca(int x,int y)
{
    if(deep[x]<deep[y])swap(x,y);
    int t=deep[x]-deep[y];
    for(int i=0;i<=16;i++)
        if(s[i]&t)x=fa[x][i];
    for(int i=16;i>=0;i--)
        if(fa[x][i]!=fa[y][i])
            x=fa[x][i],y=fa[y][i];
    if(x==y)return x;
    return fa[x][0];
}
void change(int &k,int l,int r,int x,int num)
{
    if(!k)k=++size;
    if(l==r)
    {
        mx[k]=sum[k]=num;
        return;
    }
    int mid=(l+r)>>1;
    if(x<=mid)change(ls[k],l,mid,x,num);else
        change(rs[k],mid+1,r,x,num);
    mx[k]=max(mx[ls[k]],mx[rs[k]]);
    sum[k]=sum[ls[k]]+sum[rs[k]]; 
}
int askmx(int k,int l,int r,int x,int y)
{
    if(!k)return 0;
    if(l==x&&y==r)return mx[k];
    int mid=(l+r)>>1;
    if(y<=mid)return askmx(ls[k],l,mid,x,y);else
        if(x>mid)return askmx(rs[k],mid+1,r,x,y);else
            return max(askmx(ls[k],l,mid,x,mid),askmx(rs[k],mid+1,r,mid+1,y));
}
int asksum(int k,int l,int r,int x,int y)
{
    if(!k)return 0;
    if(l==x&&y==r)return sum[k];
    int mid=(l+r)>>1;
    if(y<=mid)return asksum(ls[k],l,mid,x,y);else
        if(x>mid)return asksum(rs[k],mid+1,r,x,y);else
            return asksum(ls[k],l,mid,x,mid)+asksum(rs[k],mid+1,r,mid+1,y);
}
int solvemx(int c,int x,int f)
{
    int mx=0;
    while(belong[x]!=belong[f])
    {
        mx=max(mx,askmx(root[c],1,n,pl[belong[x]],pl[x]));
        x=fa[belong[x]][0];
    }
    mx=max(mx,askmx(root[c],1,n,pl[f],pl[x]));
    return mx;
}
int solvesum(int c,int x,int f)
{
    int sum=0;
    while(belong[x]!=belong[f])
    {
        sum+=asksum(root[c],1,n,pl[belong[x]],pl[x]);
        x=fa[belong[x]][0];
    }
    sum+=asksum(root[c],1,n,pl[f],pl[x]);
    return sum;
}
int main()
{
    s[0]=1;
    for(int i=1;i<=16;i++)
        s[i]=(s[i-1]<<1);
    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;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        ins(u,v);
    }
    dfs1(1);dfs2(1,1);
    for(int i=1;i<=n;i++)
        change(root[c[i]],1,n,pl[i],w[i]);
    for(int i=1;i<=m;i++)
    {
        char ch[5];scanf("%s",ch);
        int x,y;
        scanf("%d%d",&x,&y);
        if(ch[0]=='C')
        {
            if(ch[1]=='C')
            {
                change(root[c[x]],1,n,pl[x],0);
                c[x]=y;
                change(root[c[x]],1,n,pl[x],w[x]);
            }else
            {
                change(root[c[x]],1,n,pl[x],y);
                w[x]=y;
            }
        }else
        {
            int f=lca(x,y);
            if(ch[1]=='S')
            {
                int t=solvesum(c[x],x,f)+solvesum(c[x],y,f);
                if(c[x]==c[f])t-=w[f];
                printf("%d\n",t);
            }else
                printf("%d\n",max(solvemx(c[x],x,f),solvemx(c[x],y,f)));
        }
    }
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jack-Oran

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值