zoukankan      html  css  js  c++  java
  • [TJOI2018]异或

    这大概是这个题最蒻的一篇题解了吧,供不会可持久化$Trie$且在解决本题之前不想学会的人食用

    并不会可持久化$Trie$的蒟蒻本蒻遇到了这个题,然后用$Trie$维护的树链剖分水过去了

    数集中的数与询问的异或最大值求解思路

    思路很常见,比较好想,对数集中的所有数从高位到低位建立$01Trie$,然后贪心求解即可——设目前匹配到从高到低的第$i$位,若询问$x$的第$i$位为0,则优先进入到$Trie$树中当前节点的1方向,意义为在原最优解集上选择了所有第$i$位为1的数构成的子集作为新的最优解集,答案增加$2^{i-1}$,若当前节点的1方向不存在(原最优解集中不存在第$i$位为1的数)则进入0方向,答案不更新,询问$x$的第$i$位为1则相反,不再赘述。由进制的性质可得,贪心策略的正确性显然

    考虑本题

    子树问题常常通过$dfs$序解决,链上问题常常通过树链剖分解决,而树链剖分在其执行过程中维护了$dfs$序,故考虑通过树链剖分解决问题

    树链剖分的查询按$dfs$序是区间连续的,故只需维护$Trie$上的每个节点对应的数集(以每个节点的$dfs$序为代表),对于每个查询在数集上进行二分,判断查询区间内$0/1$方向的节点的存在性即可

    时间复杂度会比可持久化$Trie$多一个$log$,不过也是可过的

    代码

    #include<iostream>
    #include<cstdio>
    #include<vector>
    #include<algorithm>
    using namespace std;
    const int maxn=1e5+10;
    struct edge{
        int next,to;
    }e[maxn*2];
    vector<int>vc[maxn*30];
    int n,m,v[maxn],head[maxn],cnt,f[maxn],d[maxn],son[maxn];
    int tr[maxn*30][2],size[maxn],top[maxn],id[maxn],rk[maxn];
    void insert(int x,int id)
    {
        for(int now=0,i=29;i>=0;i--)
        {
            int c=(x&(1<<i))?1:0;
            now=tr[now][c]?tr[now][c]:tr[now][c]=++cnt;
            vc[now].push_back(id);
        }
    }
    int query(int x,int y,int k)
    {
        int ret=0;
        for(int now=0,i=29;i>=0;i--)
        {
            int to=(k&(1<<i))?0:1,trs=tr[now][to];
            if(upper_bound(vc[trs].begin(),vc[trs].end(),y)-lower_bound(vc[trs].begin(),vc[trs].end(),x))
                now=tr[now][to],ret+=(1<<i);
            else
                now=tr[now][to^1];
        }
        return ret;
    }
    void add(int x,int y)
    {
        e[++cnt].next=head[x];
        e[cnt].to=y;
        head[x]=cnt;
    }
    void dfs1(int x)
    {
        size[x]=1,d[x]=d[f[x]]+1;
        for(int v,i=head[x];i;i=e[i].next)
            if((v=e[i].to)!=f[x])
            {
                f[v]=x,dfs1(v),size[x]+=size[v];
                if(size[son[x]]<size[v])
                    son[x]=v;
            }
    }
    void dfs2(int x,int tp)
    {
        top[x]=tp,id[x]=++cnt,rk[cnt]=x;
        if(son[x])
            dfs2(son[x],tp);
        for(int v,i=head[x];i;i=e[i].next)
            if((v=e[i].to)!=f[x]&&v!=son[x])
                dfs2(v,v);
    }
    int sum(int x,int y,int k)
    {
        int ret=0;
        while(top[x]!=top[y])
        {
            if(d[top[x]]<d[top[y]])
                swap(x,y);
            ret=max(ret,query(id[top[x]],id[x],k));
            x=f[top[x]];
        }
        if(id[x]>id[y])
            swap(x,y);
        return ret=max(ret,query(id[x],id[y],k));
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            scanf("%d",&v[i]);
        for(int x,y,i=1;i<n;i++)
        {
            scanf("%d%d",&x,&y);
            add(x,y),add(y,x);
        }
        cnt=0,dfs1(1),dfs2(1,1),cnt=0;
        for(int i=1;i<=n;i++)
            insert(v[rk[i]],i);
        for(int op,x,y,k,i=1;i<=m;i++)
        {
            scanf("%d%d%d",&op,&x,&y);
            if(op==1)
                printf("%d
    ",query(id[x],id[x]+size[x]-1,y));
            else
            {
                scanf("%d",&k);
                printf("%d
    ",sum(x,y,k));
            }
        }
        return 0;
    }
  • 相关阅读:
    DOM几个重要的函数
    手指点赞动画
    随机颜色值
    自定义单选框radio样式
    判断是否是微信浏览器的函数
    JAVA开发微信支付-公众号支付/微信浏览器支付(JSAPI)
    微信授权获取用户openid前端实现
    CSS动画 animation与transition
    JS判断指定dom元素是否在屏幕内的方法实例
    希尔伯特曲线
  • 原文地址:https://www.cnblogs.com/ivanovcraft/p/14394743.html
Copyright © 2011-2022 走看看