Loading

cf-BitwiseXor

//https://codeforces.com/gym/102331/problem/B

#include <cstdio>
#include <algorithm>

using namespace std;

struct trie
{
  long long cnt;
  int son[2];
}tree[20000010];

int n,num=1;

long long k;

long long a[300010],f[300010];

void add(long long x,long long y)
{
  int u=1;
  for(int i=60;i>=0;i--)
  {
    int now=(x>>i)&1;
    if(!tree[u].son[now]) tree[u].son[now]=++num;
    u=tree[u].son[now];
    tree[u].cnt+=y;
    tree[u].cnt%=998244353;
  }
}

long long query(long long x,long long y)
{
  int u=1,ans=0;
  for(int i=60;i>=0;i--)
  {
    if((y>>i)&1)
    {
      u=tree[u].son[((x>>i)&1)^1];
    }
    else
    {
      ans+=tree[tree[u].son[((x>>i)&1)^1]].cnt;
      ans%=998244353;
      u=tree[u].son[(x>>i)&1];
    }
    if(u==0) break;
  }
  if(u!=0) ans+=tree[u].cnt;
  ans%=998244353;
  return ans;
}

int main()
{
  scanf("%d%lld",&n,&k);
  for(int i=1;i<=n;i++)
  {
    scanf("%lld",&a[i]);
  }
  sort(a+1,a+n+1);
  long long ans=0;
  for(int i=1;i<=n;i++)
  {
    f[i]=1+query(a[i],k);
    f[i]%=998244353;
    add(a[i],f[i]);
  }
  for(int i=1;i<=n;i++)
  {
    ans+=f[i];
    ans%=998244353;
  }
  printf("%lld\n",ans);
}

这道题还没有想的那么简单

首先,若干个数两两异或的最小值必在排序后相邻两个取到

那么我们先排序,这样按位置找到的数一定是排好序的,我们就只用比较加入的和最大的是否满足条件

\(f_{i}\)为一定选择\(i\),并且只选择前\(i\)种的数,得到的满足两两异或大于等于\(k\)的集合数

然后我们考虑一下在已知合法的集合中加入一个大于所有数的一个数该怎么转移

显然就是考虑所有可以和集合中最大的数异或大于等于\(x\)的情况

由于我们排了序,显然转移就是

\[f_{i}=\sum_{i \oplus s \ge k}{f_{s}} \]

这下,问题在于如何找到大于等于的这些东西,显然就是传统技艺01trie

posted @ 2020-06-12 12:47  zzqDeco  阅读(106)  评论(0编辑  收藏  举报