• hdu 5390 tree (线段树套trie树)


    题目链接

    http://acm.hdu.edu.cn/showproblem.php?pid=5390

    题意

    给定一棵(n)个节点的带点权树,进行(m)次操作,每次操作属于以下两类:

    • 修改一个节点的权值.
    • 对给定的节点(u),求在(u)(1)的路径上取节点(v),能得到的(val_u⊗val_v)的最大值.
      (1≤n,m≤10^5,0≤val_i≤10^9)

    思路

    查询(x)到根节点的路径上的点的影响,所以修改一个点会影响它的子树的答案。考虑按(dfs)序来维护一棵线段树,每次将修改更新到它的(dfs)序区间。找异或的最大值也自然会往(Trie)树上靠。所以我们可以用线段树套(trie)树,线段树的每一个结点维护一棵(01)字典树,每次结点修改权值,在它的dfs序区间更新字典树即可。时间复杂度为(32nlog_2n)
    查询有两种方法,一种是在修改时设个懒惰标记,然后查询的时候一路推下来到叶子节点再做查询,这种方法空间开销非常大。
    另一种是不做懒惰标记,可以只在线段树的这个节点的(Trie)树上进行删除和插入操作,然后单点询问时对线段树路径上的每个节点的(Trie)树都贪心地求解一次取最大值即可。
    用第二种方法的话trie树的大小就要开到(32nlog_2n),会超内存。先附上这种方法的代码

    #include<bits/stdc++.h>
    using namespace std;
    const int maxx = 1e5+10;
    vector<int>ma[maxx];
    int w[maxx];
    struct Trie
    {
        int trie[32*20*maxx][2],tot;
        int sum[32*20*maxx];
        void init()
        {
            tot=0;
            trie[0][0]=trie[0][1]=sum[0]=0;
        }
        void update(int rt,int x,int c)
        {
            for(int i=31;i>=0;i--)
            {
                int id=(x>>i)&1;
                if(!trie[rt][id])
                {
                    trie[rt][id]=++tot;
                    trie[tot][0]=trie[tot][1]=sum[tot]=0;
                }
                rt=trie[rt][id];
                sum[rt]+=c;
            }
        }
        int query(int rt,int x)
        {
            int res=0;
            for(int i=31;i>=0;i--)
            {
                int id=(x>>i)&1;
                if(trie[rt][id^1]&&sum[trie[rt][id^1]])rt=trie[rt][id^1],res+=(1<<i);
                else rt=trie[rt][id];
            }
            return res;
        }
    }trie;
    struct Tree
    {
        int t[maxx<<2],ans;
        void build(int l,int r,int rt)
        {
            t[rt]=++trie.tot;
            if(l==r)return;
            int mid=(l+r)/2;
            build(l,mid,rt*2);
            build(mid+1,r,rt*2+1);
        }
        void update(int l,int r,int p,int q,int x,int c,int rt)
        {
            if(p<=l&&r<=q)
            {
                trie.update(t[rt],x,c);
                return;
            }
            int mid=(l+r)/2;
            if(p<=mid)update(l,mid,p,q,x,c,rt*2);
            if(q>mid)update(mid+1,r,p,q,x,c,rt*2+1);
        }
        void query(int l,int r,int x,int c,int rt)
        {
            ans=max(ans,trie.query(t[rt],c));
            if(l==r)return;
            int mid=(l+r)/2;
            if(x<=mid)query(l,mid,x,c,rt*2);
            else query(mid+1,r,x,c,rt*2+1);
        }
    }tree;
    int in[maxx],out[maxx],cnt;
    void dfs(int u,int fa)
    {
        in[u]=++cnt;
        for(int i=0;i<ma[u].size();i++)
        {
            int v=ma[u][i];
            if(v==fa)continue;
            dfs(v,u);
        }
        out[u]=cnt;
    }
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            int n,m;
            scanf("%d%d",&n,&m);
            for(int i=1;i<=n;i++)ma[i].clear();
            int x;
            for(int i=2;i<=n;i++)
            {
                scanf("%d",&x);
                ma[x].push_back(i);
                ma[i].push_back(x);
            }
            cnt=0;
            dfs(1,0);
            for(int i=1;i<=n;i++)scanf("%d",&w[i]);
            trie.init();
            tree.build(1,n,1);
            for(int i=1;i<=n;i++)
                tree.update(1,n,in[i],out[i],w[i],1,1);
            int op,y;
            while(m--)
            {
                scanf("%d",&op);
                if(op==0)
                {
                    scanf("%d%d",&x,&y);
                    tree.update(1,n,in[x],out[x],w[x],-1,1);
                    w[x]=y;
                    tree.update(1,n,in[x],out[x],w[x],1,1);
                }
                else
                {
                    scanf("%d",&x);
                    tree.ans=0;
                    tree.query(1,n,in[x],w[x],1);
                    printf("%d
    ",tree.ans);
                }
            }
        }
        return 0;
    }
    

    因为第二种方法不用懒惰标记,线段树上每个节点实际上都是独立的,所以我们可以考虑在线段树上离线做,对每个节点,把有关的各种删除,插入和更新答案的操作按顺序做一遍就行了。用(vector)按顺序保存节点的所有操作,最后遍历整颗线段树,每到一个节点就清空(Trie)树,将该结点的操作依次维护到一棵(trie)上。这样只需要维护一棵(trie),空间复杂度就优化到(32n)

    #include<bits/stdc++.h>
    using namespace std;
    const int maxx = 1e5+10;
    vector<int>ma[maxx];
    int w[maxx];
    struct node{int op,x,c;};
    int ans[maxx];
    struct Trie
    {
        int trie[32*maxx][2],tot;
        int sum[32*maxx];
        void init()
        {
            tot=0;
            trie[0][0]=trie[0][1]=sum[0]=0;
        }
        void update(int x,int c)
        {
            int rt=0;
            for(int i=31;i>=0;i--)
            {
                int id=(x>>i)&1;
                if(!trie[rt][id])
                {
                    trie[rt][id]=++tot;
                    trie[tot][0]=trie[tot][1]=sum[tot]=0;
                }
                rt=trie[rt][id];
                sum[rt]+=c;
            }
        }
        int query(int x)
        {
            int rt=0,res=0;
            for(int i=31;i>=0;i--)
            {
                int id=(x>>i)&1;
                if(trie[rt][id^1]&&sum[trie[rt][id^1]])rt=trie[rt][id^1],res+=(1<<i);
                else rt=trie[rt][id];
            }
            return res;
        }
    }trie;
    struct Tree
    {
        vector<node>t[maxx<<2];
        void build(int l,int r,int rt)
        {
            t[rt].clear();
            if(l==r)return;
            int mid=(l+r)/2;
            build(l,mid,rt*2);
            build(mid+1,r,rt*2+1);
        }
        void update(int l,int r,int p,int q,int x,int c,int rt)
        {
            if(p<=l&&r<=q)
            {
                t[rt].push_back(node{0,x,c});
                return;
            }
            int mid=(l+r)/2;
            if(p<=mid)update(l,mid,p,q,x,c,rt*2);
            if(q>mid)update(mid+1,r,p,q,x,c,rt*2+1);
        }
        void query(int l,int r,int x,int c,int id,int rt)
        {
            t[rt].push_back(node{1,c,id});
            if(l==r)return;
            int mid=(l+r)/2;
            if(x<=mid)query(l,mid,x,c,id,rt*2);
            else query(mid+1,r,x,c,id,rt*2+1);
        }
        void getans(int l,int r,int rt)
        {
            trie.init();
            for(int i=0;i<t[rt].size();i++)
            {
                int op=t[rt][i].op,x=t[rt][i].x,c=t[rt][i].c;
                if(!op)trie.update(x,c);
                else ans[c]=max(ans[c],trie.query(x));
            }
            if(l==r)return;
            int mid=(l+r)/2;
            getans(l,mid,rt*2);
            getans(mid+1,r,rt*2+1);
        }
    }tree;
    int in[maxx],out[maxx],cnt;
    void dfs(int u,int fa)
    {
        in[u]=++cnt;
        for(int i=0;i<ma[u].size();i++)
        {
            int v=ma[u][i];
            if(v==fa)continue;
            dfs(v,u);
        }
        out[u]=cnt;
    }
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            int n,m;
            scanf("%d%d",&n,&m);
            for(int i=1;i<=n;i++)ma[i].clear();
            int x;
            for(int i=2;i<=n;i++)
            {
                scanf("%d",&x);
                ma[x].push_back(i);
                ma[i].push_back(x);
            }
            cnt=0;
            dfs(1,0);
            for(int i=1;i<=n;i++)scanf("%d",&w[i]);
            trie.init();
            tree.build(1,n,1);
            for(int i=1;i<=n;i++)
                tree.update(1,n,in[i],out[i],w[i],1,1);
            int op,y,q=0;
            while(m--)
            {
                scanf("%d",&op);
                if(op==0)
                {
                    scanf("%d%d",&x,&y);
                    tree.update(1,n,in[x],out[x],w[x],-1,1);
                    w[x]=y;
                    tree.update(1,n,in[x],out[x],w[x],1,1);
                }
                else
                {
                    scanf("%d",&x);
                    ans[++q]=0;
                    tree.query(1,n,in[x],w[x],q,1);
                }
            }
            tree.getans(1,n,1);
            for(int i=1;i<=q;i++)
                printf("%d
    ",ans[i]);
        }
        return 0;
    }
    
  • 相关阅读:
    CAAnimation动画相关文章
    [源代码]List的增加与删除
    [原创]c# 类中 Collection 字段初始化的特殊之处
    c# 多数值区间判断是否有重叠
    将M个客服随机分配给N个客户
    c# IList.ToList()后更改元素值会不会影响原列表的值
    java单元测试之Mock静态方法
    java的Spring学习2- junit和mock
    java mybatis学习二
    java8 方法引用与lambda
  • 原文地址:https://www.cnblogs.com/HooYing/p/12583785.html
Copyright © 2020-2023  润新知