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\n",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\n",ans[i]);
    }
    return 0;
}
posted @ 2020-03-27 21:38  灰灰烟影  阅读(206)  评论(0编辑  收藏  举报