题意:

在 [0,2n) 区间内任取一个数 X,依次异或 m 个本区间内的数,并在某次异或之前或之后做一次 f 操作,即将当前的 X 循环左移 (在 n 位的范围内)。求选择哪个数可以使在不同时候异或的答案的最小值最大。

分析:

首先,题目可以转化为:X 异或这 m 个数中的前 k 个和循环右移 1 位后的 (m-k) 个数后的最小值最大。根据 k 的取值不同,我们可以得到 (m+1) 个不同的这样的异或方案。根据异或的交换律和结合律,这 (m+1) 个方案就相当于那 m 个数异或后的数与 X 异或。即 $X\oplus A\oplus B\oplus C=X\oplus(A\oplus B\oplus C)$,所以我们将题目转化为:求 {X 与 (m+1) 个数中的某一个异或后结果的最小值}max

很自然地想到:策略 1: 如果 X 的某一位无论异或(m+1)个数中的哪一个,那一位都不变,我们就尽量让这一位异或之后为 1。
我们继续分析:策略 2: 如果 X 的某一位异或不同的数,结果都不同,那么这时我们就需要使后续位异或结果尽量大 (即尽量多遇到策略一)。
因此,这种只跟后续状态有关的问题,我们应该用 DP 解决,而 0 和 1 的状态分布又神似二叉树,所以我们可以尝试在 trie 树上 DP

把那 (m+1) 个数放进 trie 树,较高位更靠近根节点,那么对于每一个 X,我们都可以从 trie 数中找到一条路径使路径代表的值与 X 的异或值最小。比如 X 的最高位为 1,而 trie 树的根节点有 0 和 1 两个子节点,那么我们的路径肯定会走向 1 这个子节点,从而获得更小的异或值。

那么如何求最小值的最大值呢?二分似乎是行不通的。结合开始分析时我们想到的两个策略,我们发现:

如果 trie 树的某个节点只有左子节点或右子节点,对应着策略 1;两个节点都有的对应着策略 2。在 DP 时我们就可以结合这两个策略,更新 trie 树每个节点对应应该选什么数字可以使往下的异或值最大。


#include <algorithm>
#include <cstring>
#include <iostream>
#include <cstdio>

#define MX 3000004
#define ls tre[node].s[0]
#define rs tre[node].s[1]

using namespace std;

typedef struct trrr
{
    int fa,s[2],x,ans,num,dep;
}trie;

trie tre[MX];
int n,m,a[MX],b[MX];
int fl[MX],fr[MX];
int nn;

int f(int x)
{
    return ((x<<1)%(1<<n)+(x<<1)/(1<<n));
}

void input()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)scanf("%d",&a[i]);
}

void insrt(int node,int len,int x,int fa)
{
    tre[node].fa=fa,tre[node].dep=len;
    if(len==-1)return;
    int nxp=!!(x&(1<<len));
    if(tre[node].s[0]&&nxp==0)tre[tre[node].s[0]].x=nxp,insrt(tre[node].s[0],len-1,x,node);
    else if(tre[node].s[1]&&nxp==1)tre[tre[node].s[1]].x=nxp,insrt(tre[node].s[1],len-1,x,node);
    else
    {
        tre[node].s[nxp]=++nn;
        tre[tre[node].s[nxp]].x=nxp;
        insrt(tre[node].s[nxp],len-1,x,node);
    }
}

void build()
{
    for(int i=1;i<=m;i++)fl[i]=fl[i-1]^a[i];
    for(int i=m;i>=1;i--)fr[i]=fr[i+1]^a[i];
    for(int i=0;i<=m;i++)b[i]=f(fl[i])^fr[i+1];
    for(int i=0;i<=m;i++)insrt(0,n-1,b[i],0);
}

void dfs(int node)
{
    if(ls)dfs(ls);
    if(rs)dfs(rs);
    if(tre[node].s[0]==0&&tre[node].s[1]==0)
    {
        tre[node].ans=0;
        tre[node].num=1;
    }
    else if(tre[node].s[0]==0&&tre[node].s[1])
    {
        tre[node].ans=tre[rs].ans|(1<<tre[node].dep);
        tre[node].num=tre[rs].num;
    }
    else if(tre[node].s[0]&&tre[node].s[1]==0)
    {
        tre[node].ans=tre[ls].ans|(1<<tre[node].dep);
        tre[node].num=tre[ls].num;
    }
    else
    {
        tre[node].ans=max(tre[tre[node].s[0]].ans,tre[tre[node].s[1]].ans);
        if(tre[ls].ans<tre[rs].ans)tre[node].num=tre[rs].num;
        else if(tre[ls].ans>tre[rs].ans)tre[node].num=tre[ls].num;
        else tre[node].num=tre[ls].num+tre[rs].num;
    }
}

int main()
{
    freopen("big.in","r",stdin);
    freopen("big.out","w",stdout);
    input();
    build();
    dfs(0);
    cout<<(tre[0].ans)<<endl<<(tre[0].num)<<endl;
    return 0;
}
分类: 文章

0 条评论

发表回复

Avatar placeholder

您的电子邮箱地址不会被公开。 必填项已用 * 标注