【BZOJ2243】【SDOI2011】染色(树链剖分+线段树)

题目描述

传送门

题解

树链剖分一样就能看出来,更重要的是线段树的处理。
p数组与sum同步,.l/.r分别表示这个区间左端点和右端点的颜色。然后各种乱搞。
查询的时候,在链与链之间,如果颜色一样的话,则需要使当前答案-1。
一节微机课没搞出来,回去之后数学课想了一想,晚上终于调出来了。提交的时候行数200+,删去注释之后只有170+了。。。

代码

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;

const int max_n=1e5+5;
const int max_e=max_n*2;
const int max_N=max_n*4;

int n,m,x,y,N,u,t,ans;
int color[max_n],size[max_n],h[max_n],father[max_n],son[max_n];
int top[max_n],num[max_n],tree[max_n];
int point[max_n],next[max_e],v[max_e],tot;
int sum[max_N],delta[max_N],interval[max_n];
struct hp{
    int l,r;
}p[max_N];

inline void add(int x,int y){
    ++tot; next[tot]=point[x]; point[x]=tot; v[tot]=y;
    ++tot; next[tot]=point[y]; point[y]=tot; v[tot]=x;
}

inline void dfs_1(int x,int fa,int dep){
    size[x]=1; h[x]=dep; father[x]=fa;
    int maxson=0;
    for (int i=point[x];i;i=next[i])
      if (v[i]!=fa){
        dfs_1(v[i],x,dep+1);
        size[x]+=size[v[i]];
        if (size[v[i]]>maxson){
            maxson=size[v[i]];
            son[x]=v[i];
        }
      }
}

inline void dfs_2(int x,int fa){
    if (son[fa]!=x) top[x]=x;
    else top[x]=top[fa];
    num[x]=++N;
    if (son[x]) dfs_2(son[x],x);
    for (int i=point[x];i;i=next[i])
      if (v[i]!=fa&&v[i]!=son[x])
        dfs_2(v[i],x);
}

inline void update(int now){
    if (p[now<<1].r==p[now<<1|1].l)
      sum[now]=sum[now<<1]+sum[now<<1|1]-1;
    else sum[now]=sum[now<<1]+sum[now<<1|1];
    p[now].l=p[now<<1].l;
    p[now].r=p[now<<1|1].r;
}

inline void pushdown(int now,int l,int r,int mid){
    if (delta[now]){
        sum[now<<1]=1;
        delta[now<<1]=delta[now];
        p[now<<1].l=p[now<<1].r=delta[now];
        sum[now<<1|1]=1;
        delta[now<<1|1]=delta[now];
        p[now<<1|1].l=p[now<<1|1].r=delta[now];
        delta[now]=0;
    }
}

inline void build(int now,int l,int r){
    int mid=(l+r)>>1;
    if (l==r){
        interval[l]=now;
        sum[now]=1;
        p[now].l=p[now].r=color[tree[l]];
        return;
    }
    build(now<<1,l,mid);
    build(now<<1|1,mid+1,r);
    update(now);
}

inline void interval_change(int now,int l,int r,int lrange,int rrange,int v){
    int mid=(l+r)>>1;
    if (lrange<=l&&r<=rrange){
        sum[now]=1;
        p[now].l=p[now].r=v;
        delta[now]=v;
        return;
    }
    pushdown(now,l,r,mid);
    if (lrange<=mid)
      interval_change(now<<1,l,mid,lrange,rrange,v);
    if (mid+1<=rrange)
      interval_change(now<<1|1,mid+1,r,lrange,rrange,v);
    update(now);
}

inline int query(int now,int l,int r,int lrange,int rrange){
    int mid=(l+r)>>1,ans=0;
    if (lrange<=l&&r<=rrange) return sum[now];
    pushdown(now,l,r,mid);
    bool pd1=false,pd2=false;
    if (lrange<=mid)
      ans+=query(now<<1,l,mid,lrange,rrange),pd1=true;
    if (mid+1<=rrange)
      ans+=query(now<<1|1,mid+1,r,lrange,rrange),pd2=true;
    if (pd1&&pd2&&p[now<<1].r==p[now<<1|1].l)
      ans--;
    return ans;
}

inline void CHANGE(int u,int t,int x){
    int f1=top[u],f2=top[t];
    while (f1!=f2){
        if (h[f1]<h[f2]){
            swap(u,t);
            swap(f1,f2);
        }
        interval_change(1,1,N,num[f1],num[u],x);
        u=father[f1];
        f1=top[u];
    }
    if (num[u]>num[t]) swap(u,t);
    interval_change(1,1,N,num[u],num[t],x);
}

inline int QUERY(int u,int t){
    int f1=top[u],f2=top[t],ans=0;
    while (f1!=f2){
        if (h[f1]<h[f2]){
            swap(u,t);
            swap(f1,f2);
        }
        ans+=query(1,1,N,num[f1],num[u]);

        //pushdown
        int k=query(1,1,N,num[f1],num[f1]);
        k=query(1,1,N,num[father[f1]],num[father[f1]]);

        if (p[interval[num[f1]]].l==p[interval[num[father[f1]]]].l) 
          ans--;
        u=father[f1];
        f1=top[u];
    }
    if (num[u]>num[t]) swap(u,t);
    ans+=query(1,1,N,num[u],num[t]);
    return ans;
}

int main(){
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;++i)
      scanf("%d",&color[i]);
    for (int i=1;i<n;++i){
        scanf("%d%d",&x,&y);
        add(x,y);
    }

    dfs_1(1,0,1);
    dfs_2(1,0);
    for (int i=1;i<=n;++i)
      tree[num[i]]=i;

    build(1,1,N);

    for (int i=1;i<=m;++i){
        char ch=getchar();
        while (ch!='C'&&ch!='Q') ch=getchar();
        if (ch=='C'){
            scanf("%d%d%d",&u,&t,&x);
            CHANGE(u,t,x);
        }
        else{
            scanf("%d%d",&u,&t);
            ans=QUERY(u,t);
            printf("%d\n",ans);
        }
    }
}

总结

①关于线段树的任何操作(包括有时候乱搞的直接查询编号),都要思考一下有没有pushdown,即当前也许只是打了标记,没有更新到最新值。
②自己写input不要写错了。

你可能感兴趣的:(线段树,树链剖分,bzoj,SDOI)