【bzoj2243】 [SDOI2011]染色

本文详细介绍了树链剖分与线段树的结合应用,通过具体实例讲解了如何利用这两种数据结构解决复杂问题。重点在于树链剖分中节点位置的正确处理及线段树的维护方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

树链剖分裸题,建线段树的时候维护左端点右端点然后合并信息的时候中间相同的话减1,注意在树链剖分的时候也是如此
这道题调了好久,一定要注意在树链剖分时每个点的位置是用num数组存储的,所以每次query或者change的时候都一定要用num[]来表示节点,而不是直接用节点表示

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>

using namespace std;

const int N=100010;
int qx,n,m,te,sz;
int tp[N],head[N],num[N],val[N],tree[N],h[N],fa[N],son[N],size[N];

struct seg{
    int l,r,sum,tag,lcol,rcol;
}tr[400010];

struct edge{
    int v,next;
}e[200010];

inline int F()
{
    register int aa,bb;register char ch;
    while(ch=getchar(),(ch<'0'||ch>'9')&&ch!='-');ch=='-'?aa=bb=0:(aa=ch-'0',bb=1);
    while(ch=getchar(),ch>='0'&&ch<='9')aa=(aa<<3)+(aa<<1)+ch-'0';return bb?aa:-aa;
}

void clear()
{
    sz=te=0;memset(head,0,sizeof(head));
    h[1]=fa[1]=1;size[0]=0;
    memset(son,0,sizeof(son));
}
void add(int u,int v)
{
    e[++te].v=v;
    e[te].next=head[u];
    head[u]=te;
}
void pushdown(int k)
{
    if (tr[k].tag==-1||tr[k].l==tr[k].r)return;
    tr[k<<1].lcol=tr[k<<1].rcol=tr[k<<1|1].lcol=tr[k<<1|1].rcol=tr[k].tag;
    tr[k<<1].sum=tr[k<<1|1].sum=1;
    tr[k<<1].tag=tr[k<<1|1].tag=tr[k].tag;
    tr[k].tag=-1;
}
void updata(int k)
{
    tr[k].lcol=tr[k<<1].lcol,tr[k].rcol=tr[k<<1|1].rcol;
    tr[k].sum=tr[k<<1].sum+tr[k<<1|1].sum;
    if (tr[k<<1].rcol==tr[k<<1|1].lcol)tr[k].sum--;
}
void dfs1(int x)
{
    size[x]=1;
    for (int i=head[x];i;i=e[i].next)
    {
        int v=e[i].v;
        if (v==fa[x])continue;
        fa[v]=x;
        h[v]=h[x]+1;
        dfs1(v);
        size[x]+=size[v];
        if (size[v]>size[son[x]])son[x]=v;
    }
}
void dfs2(int x,int chain)
{
    tp[x]=chain;
    num[x]=++sz;
    if (!son[x])return ;
    dfs2(son[x],chain);
    for (int i=head[x];i;i=e[i].next)
    {
        int v=e[i].v;
        if (v==fa[x]||v==son[x])continue;
        dfs2(v,v);
    }
}
void build(int k,int l,int r)
{
    tr[k].l=l;tr[k].r=r;tr[k].tag=-1;
    if (l==r)
    {
        tr[k].lcol=tr[k].rcol=tree[l];
        tr[k].sum=1;return;
    }
    int mid=(l+r)>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
    updata(k);
}
void change(int k,int x,int y,int z)
{
    int l=tr[k].l,r=tr[k].r;
    if (x<=l&&r<=y)
    {
        tr[k].lcol=tr[k].rcol=tr[k].tag=z;
        tr[k].sum=1;return;
    }
    pushdown(k);
    int mid=(l+r)>>1;
    if (x<=mid)change(k<<1,x,y,z);
    if (y>mid)change(k<<1|1,x,y,z);
    updata(k);
}
int queryx(int k,int x)
{
    int l=tr[k].l,r=tr[k].r;
    if (l==r)
    {
        return tr[k].lcol;
    }
    pushdown(k);
    int mid=(l+r)>>1;
    if (x<=mid)return queryx(k<<1,x);
    if (x>mid)return queryx(k<<1|1,x);
    updata(k);
}
void querysum(int k,int x,int y)
{
    int l=tr[k].l,r=tr[k].r;
    if (x<=l&&r<=y)
    {
        qx+=tr[k].sum;return;
    }
    pushdown(k);
    int mid=(l+r)>>1;
    if (x<=mid)querysum(k<<1,x,y);
    if (y>mid)querysum(k<<1|1,x,y);
    if (x<=mid&&y>mid&&tr[k<<1].rcol==tr[k<<1|1].lcol)qx--;
    updata(k);
}
void solvechange(int x,int y,int z)
{
    while (tp[x]!=tp[y])
    {
        if (h[tp[x]]<h[tp[y]])swap(x,y);
        change(1,num[tp[x]],num[x],z);
        x=fa[tp[x]];
    }
    if (num[x]>num[y])swap(x,y);
    change(1,num[x],num[y],z);
}
void solvesum(int x,int y)
{
    qx=0;
    while(tp[x]!=tp[y])
    {
        if (h[tp[x]]<h[tp[y]])swap(x,y);
        querysum(1,num[tp[x]],num[x]);
        int a1=queryx(1,num[tp[x]]),a2=queryx(1,num[fa[tp[x]]]);
//      cout<<a1<<' '<<a2<<' '<<qx<<endl;
        if (a1==a2)qx--;
        x=fa[tp[x]];
    }
    if (num[x]>num[y])swap(x,y);
    querysum(1,num[x],num[y]);
}
int main()
{
//  freopen("std.in","r",stdin);
    clear();
    int u,v,x,y,z;
    n=F(),m=F();
    for (int i=1;i<=n;++i)val[i]=F();
    for (int i=1;i<n;++i)
    {
        u=F(),v=F();
        add(u,v),add(v,u);
    }
    dfs1(1),dfs2(1,1);
    for (int i=1;i<=n;++i)tree[num[i]]=val[i];
//  for (int i=1;i<=n;i++)cout<<num[i]<<' ';
//  cout<<endl;
    build(1,1,n);
//  for (int i=1;i<=20;++i)
//  cout<<tr[i].l<<' '<<tr[i].r<<' '<<tr[i].lcol<<' '<<tr[i].rcol<<' '<<tr[i].sum<<endl;
    char s[4];
    for (int i=1;i<=m;++i)
    {
        scanf("%s",s);
        if (s[0]=='Q')
        {
            x=F(),y=F();
            solvesum(x,y);
//          if (i==7)
//          for (int i=1;i<=20;++i)cout<<tr[i].l<<' '<<tr[i].r<<' '<<tr[i].lcol<<' '<<tr[i].rcol<<' '<<tr[i].sum<<endl;
            printf("%d\n",qx);
        }
        else 
        {
            x=F(),y=F(),z=F();
            solvechange(x,y,z);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值