BZOJ 2243 [SDOI2011]染色 树链剖分+LCA+区间合并线段树

题意:
给定一棵有n个节点的无根树和m个操作,操作有2类:
1、将节点a到节点b路径上所有点都染成颜色c;
2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”、“222”和“1”。
请你写一个程序依次完成这m个操作。
思路:
这道题敲了很久…..(好恶心….
但是AC了真的很exciting
就是要考虑到区间合并,两条链进行合并的时候要判断
ft[x][i]就是从x节点向上跳2^i次方个节点所到达的节点
就是运用倍增的思想…倍增思想的请看郭大侠的讲解http://www.bilibili.com/video/av4419432/

这道题有两个操作
1、将节点a到节点b路径上所有点都染成颜色c 这个很简单,就是正常的树剖+线段树+lazy就OK了
还有LCA有点麻烦
下一个
2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”、“222”和“1”。这里需要+区间合并
我们先剖分整棵树,注意再剖分的过程中进行倍增的预处理,因为我们要求LCA
然后线段树query拆分区间的时候要注意一下
每次查询和插入的时候我们通过把区间分为[L,LCA] and [LCA,R]来处理
这么想想….好像是水题QAQ
这里写图片描述
就是区间合并和求LCA的时候注意一下就行了

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#include<queue>
#include<stack>
#include<string>
#include<vector>
#include<map>
#include<set>
using namespace std;
#define lowbit(x) (x&(-x))
typedef long long LL;
#define maxn 200005
const int inf=(1<<28)-1;
int A[maxn];
struct node
{
    int next,to;
}e[maxn*2];
int h[maxn],tot;
void add_edge(int a,int b)
{
    e[++tot].next=h[a];
    e[tot].to=b;
    h[a]=tot;
}
//Graph*******************************
int ft[maxn][18];
int son[maxn],fa[maxn],siz[maxn],deep[maxn];
//求重儿子 父亲节点 节点子树大小 深度 和倍增节点 
void dfs1(int u,int pre,int dep)
{
    deep[u]=dep;fa[u]=pre;
    siz[u]=1;
    for(int i=1;i<=17;++i)
    {
        if(deep[u]<(1<<i)) break;
        ft[u][i]=ft[ft[u][i-1]][i-1];
    }
    for(int i=h[u];~i;i=e[i].next)
    {
        int v=e[i].to;
        if(v!=pre)
        {
            ft[v][0]=u;
            dfs1(v,u,dep+1);
            siz[u]+=siz[v];
            if(son[u]==-1||siz[son[u]]<siz[v])
            son[u]=v;
        }
    }
}
int tid[maxn],Rank[maxn],top[maxn];
int tim;
//给节点标号和求top节点
void dfs2(int u,int tp)
{
    top[u]=tp;
    tid[u]=++tim;
    Rank[tim]=u;
    if(son[u]==-1) return ;
    dfs2(son[u],tp);
    for(int i=h[u];~i;i=e[i].next)
    {
        int v=e[i].to;
        if(v!=fa[u]&&v!=son[u])
        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<18;++i)
    if(t&(1<<i)) x=ft[x][i];
    for(int i=17;i>=0;--i)
    if(ft[x][i]!=ft[y][i])
    {
        x=ft[x][i];
        y=ft[y][i];
    }
    if(x==y) return x;
    return ft[x][0];
}
//树剖********************************* 
struct Segment
{
    int lc,rc,sum,lazy;
}tree[maxn*4];
void Push_up(int rt)
{
    tree[rt].lc=tree[rt*2].lc;
    tree[rt].rc=tree[rt*2+1].rc;
    tree[rt].sum=tree[rt*2].sum+tree[rt*2+1].sum;
    if(tree[rt*2].rc==tree[rt*2+1].lc)
    tree[rt].sum--;
}
void Copy(int rt1,int rt)
{
    tree[rt1].lc=tree[rt].lazy;
    tree[rt1].rc=tree[rt].lazy;
    tree[rt1].lazy=tree[rt].lazy;
    tree[rt1].sum=1;
}
void Push_down(int rt)
{
    if(tree[rt].lazy==-1) return ;
    Copy(rt*2,rt);
    Copy(rt*2+1,rt);
    tree[rt].lazy=-1;
}
void build(int l,int r,int rt)
{
    tree[rt].lazy=-1;
    if(l==r)
    {
        tree[rt].lc=tree[rt].rc=A[Rank[l]];
        tree[rt].sum=1;
        return ;
    }
    int mid=(l+r)/2;
    build(l,mid,rt*2);
    build(mid+1,r,rt*2+1);
    Push_up(rt);
}
void Insert(int left,int right,int l,int r,int rt,int c)
{
    if(left<=l&&r<=right)
    {
        tree[rt].lc=tree[rt].rc=c;
        tree[rt].sum=1;tree[rt].lazy=c;
        return ;
    }
    Push_down(rt);
    int mid=(l+r)/2;
    if(left<=mid)
    Insert(left,right,l,mid,rt*2,c);
    if(right>mid)
    Insert(left,right,mid+1,r,rt*2+1,c);
    Push_up(rt);
}
int query(int left,int right,int l,int r,int rt)
{
    if(left<=l&&r<=right) return tree[rt].sum;
    Push_down(rt);
    int mid=(l+r)/2;
    int tmp=0;
    if(left>mid)
    tmp=query(left,right,mid+1,r,rt*2+1);
    else if(right<=mid)
    tmp=query(left,right,l,mid,rt*2);
    else
    {
        tmp=query(left,right,l,mid,rt*2);
        tmp+=query(left,right,mid+1,r,rt*2+1);
        if(tree[rt*2].rc==tree[rt*2+1].lc) tmp--;
    }
    Push_up(rt);
    return tmp;
}
int find(int pos,int l,int r,int rt)
{
    if(l==r) return tree[rt].lc;
    Push_down(rt);
    int mid=(l+r)/2,tmp;
    if(pos>mid) return find(pos,mid+1,r,rt*2+1);
    else return find(pos,l,mid,rt*2);
    Push_up(rt);
    return tmp;
}
//Segment*********************************
void init()
{
    tot=0,tim=0;
    memset(h,-1,sizeof(h));
    memset(son,-1,sizeof(son));
}
int n;
void Change(int x,int t,int c)
{
    while(top[x]!=top[t])
    {
        Insert(tid[top[x]],tid[x],1,n,1,c);
        x=fa[top[x]];
    }
    Insert(tid[t],tid[x],1,n,1,c);
}
int Solve(int x,int t)
{
    int res=0;
    while(top[x]!=top[t])
    {
        res+=query(tid[top[x]],tid[x],1,n,1);
        if(find(tid[top[x]],1,n,1)==find(tid[ft[top[x]][0]],1,n,1)) res--;
        x=fa[top[x]];
    }
    res+=query(tid[t],tid[x],1,n,1);
    return res;
}
int main()
{
    init();
    int m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i) scanf("%d",&A[i]);
    for(int i=1;i<n;++i)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        add_edge(u,v);
        add_edge(v,u);
    }
    dfs1(1,0,0);
    dfs2(1,1);
    build(1,n,1);
    for(int i=1;i<=m;++i)
    {
        char str[5];
        scanf("%s",str);
        if(str[0]=='C')
        {
            int u,v,c;
            scanf("%d%d%d",&u,&v,&c);
            int t=lca(u,v);
            Change(u,t,c);
            Change(v,t,c);
        }
        else
        {
            int u,v;
            scanf("%d%d",&u,&v);
            int t=lca(u,v);
            printf("%d\n",Solve(u,t)+Solve(v,t)-1);
        }
    }
    return 0;
}

你可能感兴趣的:(树链剖分+LCA)