zoukankan      html  css  js  c++  java
  • 【BZOJ】2243 [SDOI2011]染色

    【算法】树链剖分+线段树

    【题解】

    树链剖分算法:http://www.cnblogs.com/onioncyc/p/6207462.html

    定义线段树结构体有l,r,lc,rc,sum,data。

    lc表示左端颜色,rc表示右端颜色,sum表示颜色种类,data表示区间置为同一个数的标记。

    修改的时候要上推和下传,查询的时候要下传。

    我的写法是打lazy标记的时候顺便把子树的其它参数都修改完毕,方便直接调用。

    访问到有lazy标记的子树时把标记下传给左右子树并修改左右子树的其他参数。

    左右端颜色相同的处理方法见:http://blog.csdn.net/u011645923/article/details/43087133

    还是注意树链剖分后操作要使用新编号pos[i]。

    #include<cstdio>
    #include<cctype>
    #include<algorithm>
    using namespace std;
    int read()
    {
        char c;int s=0,t=1;
        while(!isdigit(c=getchar()))if(c=='-')t=-1;
        do{s=s*10+c-'0';}while(isdigit(c=getchar()));
        return s*t;
    }
    const int maxn=300010;
    int first[maxn],size[maxn],deep[maxn],f[maxn],top[maxn],pos[maxn],LC,RC,n,m,tot,dfsnum,a[maxn];
    struct edge{int u,v,from;}e[maxn*3];
    struct tree{int lc,rc,sum,l,r,data;}t[maxn*3];
    void insert(int u,int v)
    {tot++;e[tot].u=u;e[tot].v=v;e[tot].from=first[u];first[u]=tot;}
    void dfs1(int x,int fa)
    {
        size[x]=1;
        for(int i=first[x];i;i=e[i].from)
         if(e[i].v!=fa)
          {
            int y=e[i].v;
            f[y]=x;
            deep[y]=deep[x]+1;
            dfs1(y,x);
            size[x]+=size[y];
          }
    }
    void dfs2(int x,int tp,int fa)
    {
        int k=0;
        pos[x]=++dfsnum;
        top[x]=tp;
        for(int i=first[x];i;i=e[i].from)
         if(e[i].v!=fa&&size[e[i].v]>size[k])k=e[i].v;
        if(k==0)return;
        dfs2(k,tp,x);
        for(int i=first[x];i;i=e[i].from)
         if(e[i].v!=fa&&e[i].v!=k)dfs2(e[i].v,e[i].v,x);
    }
    void build(int k,int l,int r)
    {
        t[k].l=l;t[k].r=r;
        if(l==r){t[k].lc=0;t[k].rc=0;t[k].sum=0;t[k].data=0;return;}
         else
          {
              int mid=(l+r)>>1;
              build(k<<1,l,mid);
              build(k<<1|1,mid+1,r);
          }
    }
    void pushdown(int k)
    {
        if(t[k].data)
         {
             t[k<<1].sum=t[k<<1|1].sum=1;
             t[k<<1].data=t[k<<1|1].data=t[k].data;
             t[k<<1].lc=t[k<<1].rc=t[k].data;
             t[k<<1|1].lc=t[k<<1|1].rc=t[k].data;
         }
        t[k].data=0;
    }
    void pushup(int k)
    {
        t[k].lc=t[k<<1].lc;
        t[k].rc=t[k<<1|1].rc;
        t[k].sum=t[k<<1].sum+t[k<<1|1].sum;
        if(t[k<<1].rc==t[k<<1|1].lc)t[k].sum--;
    }
    void change(int k,int l,int r,int x)//区间修改需要上推&&下传 
    {
        pushdown(k);
        int left=t[k].l,right=t[k].r;
        if(l<=left&&right<=r){t[k].data=x;t[k].sum=1;t[k].lc=t[k].rc=x;}
         else
          {
              int mid=(left+right)>>1;
              if(l<=mid)change(k<<1,l,r,x);
              if(r>mid)change(k<<1|1,l,r,x);
              pushup(k);
          }
    }
    int ask(int k,int l,int r)//区间查询只需要下传 
    {
        pushdown(k);
        int left=t[k].l,right=t[k].r;
        if(l==left)LC=t[k].lc;
        if(r==right)RC=t[k].rc;
        if(l<=left&&right<=r){return t[k].sum;}
         else
          {
              int mid=(left+right)>>1,sums=0,ok=0;
              if(l<=mid)sums=ask(k<<1,l,r),ok++;
              if(r>mid)sums+=ask(k<<1|1,l,r),ok++;
              if(ok==2&&t[k<<1].rc==t[k<<1|1].lc)sums--;//只取一边的话就不需要判断了 
              return sums;
          }
    }
    void update(int x,int y,int z)
    {
        while(top[x]!=top[y])
         {
             if(deep[top[x]]<deep[top[y]])swap(x,y);
             change(1,pos[top[x]],pos[x],z);//!!!
             x=f[top[x]];
         }
        if(pos[x]>pos[y])swap(x,y);
        change(1,pos[x],pos[y],z);
    }
    int solve(int x,int y)
    {
        int sums=0,ansx=0,ansy=0;//分别表示x和y的左端点颜色 
        while(top[x]!=top[y])
         {
             if(deep[top[x]]<deep[top[y]])swap(x,y),swap(ansx,ansy);
             sums+=ask(1,pos[top[x]],pos[x]);
             if(RC==ansx)sums--;
             ansx=LC; 
             x=f[top[x]];
         }
        if(pos[x]>pos[y])swap(x,y),swap(ansx,ansy);
        sums+=ask(1,pos[x],pos[y]);
        if(ansx==LC)sums--;
        if(ansy==RC)sums--; 
        return sums;
    }
    int main()
    {
        n=read();m=read();
        for(int i=1;i<=n;i++)a[i]=read();
        for(int i=1;i<n;i++)
         {
            int u=read(),v=read();
            insert(u,v);
            insert(v,u);
         }
        dfs1(1,-1);dfs2(1,1,-1);
        build(1,1,n);
        for(int i=1;i<=n;i++)update(i,i,a[i]+1);//颜色+1避免0的问题 
        for(int i=1;i<=m;i++)
         {
             char c=getchar();
             while(!(c=='C'||c=='Q'))c=getchar();
             if(c=='C')
              {
                  int x=read(),y=read(),z=read();
                update(x,y,z+1);
              }
             else
              {
                  int x=read(),y=read();
                  printf("%d
    ",solve(x,y));
              }
         }
        return 0;
    }
    View Code
  • 相关阅读:
    CSS3 target伪类简介
    不用position,让div垂直居中
    css3 在线编辑工具 连兼容都写好了
    a标签伪类的顺序
    oncopy和onpaste
    【leetcode】1523. Count Odd Numbers in an Interval Range
    【leetcode】1518. Water Bottles
    【leetcode】1514. Path with Maximum Probability
    【leetcode】1513. Number of Substrings With Only 1s
    【leetcode】1512. Number of Good Pairs
  • 原文地址:https://www.cnblogs.com/onioncyc/p/6212523.html
Copyright © 2011-2022 走看看