zoukankan      html  css  js  c++  java
  • 【HDU4897】Little Devil I-树链剖分

    测试地址:Little Devil I
    题目大意:有一棵树,每条边一开始为白色,维护两个操作:将路径上所有的边反色(黑白互换),将所有和路径相邻(只有一个端点在路径上)的边反色,并支持询问一条路径上黑色边的数目。
    做法:本题需要用到树链剖分。
    首先路径反色很模板,就不说了。关键是路径相邻边反色,看上去很不可做,因此我们考虑等价代换。
    注意到,一条边只有一个端点在路径上时,它才会被反色,有零个或两个端点的边不受影响,这看上去就非常的像异或了。于是我们可以给路径上每个点打上标记,一条边只有在两个端点的标记总数为奇数时,才表示它在这种情况下被反色。那么在询问时,重链上的边显然可以合并出来(注意要和路径反色的标记一起考虑),而轻边不超过logn条,在线段树中分别询问这两个点的标记进行处理即可。
    于是我们就解决了这一题,时间复杂度为O(nlog2n)
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    int T,n,m,tot,first[100010];
    int fa[100010]={0},dep[100010]={0},siz[100010]={0};
    int son[100010]={0},top[100010],pos[100010],tim;
    bool tag[400010][2],cntl[400010][2],cntr[400010];
    int ans[400010];
    struct edge
    {
        int v,next;
    }e[200010];
    bool flag,lastr;
    
    void insert(int a,int b)
    {
        e[++tot].v=b;
        e[tot].next=first[a];
        first[a]=tot;
    }
    
    void dfs1(int v)
    {
        dep[v]=dep[fa[v]]+1;
        siz[v]=1,son[v]=0;
        for(int i=first[v];i;i=e[i].next)
            if (e[i].v!=fa[v])
            {
                fa[e[i].v]=v;
                dfs1(e[i].v);
                siz[v]+=siz[e[i].v];
                if (siz[e[i].v]>siz[son[v]])
                    son[v]=e[i].v;
            }
    }
    
    void dfs2(int v,int t)
    {
        top[v]=t,pos[v]=++tim;
        if (son[v]) dfs2(son[v],t);
        for(int i=first[v];i;i=e[i].next)
            if (e[i].v!=fa[v]&&e[i].v!=son[v])
                dfs2(e[i].v,e[i].v);
    }
    
    void update(int no,int l,int r,bool op)
    {
        tag[no][op]=!tag[no][op];
        if (!op) cntl[no][0]=!cntl[no][0];
        else cntl[no][1]=!cntl[no][1],cntr[no]=!cntr[no];
        if (!op) ans[no]=r-l-ans[no];
    }
    
    void pushdown(int no,int l,int r)
    {
        int mid=(l+r)>>1;
        if (tag[no][0])
        {
            update(no<<1,l,mid,0);
            update(no<<1|1,mid+1,r,0);
        }
        if (tag[no][1])
        {
            update(no<<1,l,mid,1);
            update(no<<1|1,mid+1,r,1);
        }
        tag[no][0]=tag[no][1]=0;
    }
    
    void pushup(int no)
    {
        ans[no]=ans[no<<1]+ans[no<<1|1];
        cntl[no][0]=cntl[no<<1][0];
        cntl[no][1]=cntl[no<<1][1];
        cntr[no]=cntr[no<<1|1];
        if (cntl[no<<1|1][1]^cntr[no<<1]^cntl[no<<1|1][0])
            ans[no]++;
    }
    
    void segmodify(int no,int l,int r,int s,int t,bool op)
    {
        if (l>=s&&r<=t)
        {
            update(no,l,r,op);
            return;
        }
        int mid=(l+r)>>1;
        pushdown(no,l,r);
        if (s<=mid) segmodify(no<<1,l,mid,s,t,op);
        if (t>mid) segmodify(no<<1|1,mid+1,r,s,t,op);
        pushup(no);
    }
    
    int segquery(int no,int l,int r,int s,int t)
    {
        if (l>=s&&r<=t)
        {
            bool right=0;
            if (flag)
            {
                right^=cntl[no][0]^cntl[no][1];
                right^=lastr;
            }
            else flag=1;
            lastr=cntr[no];
            return ans[no]+(int)right;
        }
        int totans=0,mid=(l+r)>>1;
        pushdown(no,l,r);
        if (s<=mid) totans+=segquery(no<<1,l,mid,s,t);
        if (t>mid) totans+=segquery(no<<1|1,mid+1,r,s,t);
        return totans;
    }
    
    bool querypoint(int no,int l,int r,int x,int op)
    {
        if (l==r)
        {
            if (op==0) return cntl[no][0];
            if (op==1) return cntl[no][1];
            if (op==2) return cntr[no];
        }
        int mid=(l+r)>>1;
        pushdown(no,l,r);
        if (x<=mid) return querypoint(no<<1,l,mid,x,op);
        else return querypoint(no<<1|1,mid+1,r,x,op);
    }
    
    void modify(int a,int b,bool op)
    {
        while(top[a]!=top[b])
        {
            if (dep[top[a]]<dep[top[b]])
                swap(a,b);
            segmodify(1,1,n,pos[top[a]],pos[a],op);
            a=fa[top[a]];
        }
        if (dep[a]>dep[b]) swap(a,b);
        if (!op&&a==b) return;
        if (!op) segmodify(1,1,n,pos[a]+1,pos[b],op);
        else segmodify(1,1,n,pos[a],pos[b],op);
    }
    
    int query(int a,int b)
    {
        int totans=0;
        while(top[a]!=top[b])
        {
            if (dep[top[a]]<dep[top[b]])
                swap(a,b);
            flag=0;
            totans+=segquery(1,1,n,pos[top[a]],pos[a]);
            bool right=0;
            right^=querypoint(1,1,n,pos[top[a]],0);
            right^=querypoint(1,1,n,pos[top[a]],1);
            right^=querypoint(1,1,n,pos[fa[top[a]]],2);
            if (right) totans++;
            a=fa[top[a]];
        }
        if (dep[a]>dep[b]) swap(a,b);
        flag=0;
        totans+=segquery(1,1,n,pos[a],pos[b]);
        return totans;
    }
    
    int main()
    {
        scanf("%d",&T);
        while(T--)
        {
            memset(first,0,sizeof(first));
            tot=0;
    
            scanf("%d",&n);
            for(int i=1;i<n;i++)
            {
                int a,b;
                scanf("%d%d",&a,&b);
                insert(a,b),insert(b,a);
            }
    
            dfs1(1);
            tim=0;
            dfs2(1,1);
    
            memset(tag,0,sizeof(tag));
            memset(cntl,0,sizeof(cntl));
            memset(cntr,0,sizeof(cntr));
            memset(ans,0,sizeof(ans));
            scanf("%d",&m);
            for(int i=1;i<=m;i++)
            {
                int op,x,y;
                scanf("%d%d%d",&op,&x,&y);
                if (op<=2) modify(x,y,op-1);
                else printf("%d
    ",query(x,y));
            }
        }
    
        return 0;
    }
  • 相关阅读:
    C#读写xml文件
    XSD(XML Schema Definition)用法实例介绍以及C#使用xsd文件验证XML格式
    C#异步批量下载文件
    echarts的markline的使用 y轴预警线
    Bootstrap-table 增删改查
    二维数组 和 稀疏数组的相互转换 及 数据存入文件中
    Bootstrap-table实现动态合并相同行
    echarts 中 参数的详讲
    BootstrapTable的简单使用教程
    遍历List 中 Map 的值
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793269.html
Copyright © 2011-2022 走看看