peiwenjun's blog 没有知识的荒原

ABC249G Xor Cards 题解

题目描述

给定 \(n\) 张卡片,第 \(i\) 张卡片正面 \(a_i\) ,反面 \(b_i\)

你可以选择若干张卡片,要求正面异或和 \(\le k\) ,求反面异或和最大值。

如果你无法选择任何卡片,输出 -1

数据范围

  • \(1\le n\le 1000,0\le k,a_i,b_i\lt 2^{30}\)

时间限制 \(\texttt{2s}\) ,空间限制 \(\texttt{1024MB}\)

分析

\(x_i=2^{30}\cdot a_i+b_i\) ,要求选择若干 \(x_i\) ,在满足前 \(30\) 位异或和 \(\le k\) 的前提下,求后 \(30\) 位异或和的最大值。

容易发现将 \(x_i\) 替换成 \(x_i\oplus x_j\) 对答案没有影响,因此可以先把 \(x_i\) 都插入到线性基中。

用另一个线性基存储所有可以随意选的 \(b_i\)

如果插入 \(x_i\) 的过程走到后 \(30\) 位,我们可以直接把当前 \(b_i\) 插入这个线性基。

先消成上三角基,统计答案时枚举每一位,假设因为没选 \(a_i\) 所以没有顶住 \(k\) 的上界。

高位的限制相当于钦定若干个数必选。在线性基中选若干个数与 \(x\) 的异或最大值是线性基的经典问题,直接按位贪心即可。

实现细节:

  • 由于答案可能为 \(0\) ,所以不能想当然拿答案为 \(0\) 判无解,最简单的判断方法是看 \(a_i\) 的异或最小值是否 \(\le k\)
  • 卡位没有判断 \(a_i\) 异或和恰好等于 \(k\) 的情况,记得特判。

时间复杂度 \(\mathcal O(n\log V+\log^3V)\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1005,all=(1<<30)-1;
int k,n,flg,res;
int a[maxn],b[maxn],v[maxn];
struct basis
{
    int v[30];
    void insert(int x)
    {
        for(int i=29;i>=0;i--)
        {
            if(!(x>>i&1)) continue;
            if(!v[i]) return v[i]=x,void();
            x^=v[i];
        }
    }
    int query(int x)
    {
        for(int i=29;i>=0;i--) x=max(x,x^v[i]);
        return x;
    }
}now;
void insert(int x)
{
    for(int i=59;i>=30;i--)
    {
        if(!(x>>i&1)) continue;
        if(!v[i]) return v[i]=x,void();
        x^=v[i];
    }
    flg=1,now.insert(x);
}
signed main()
{
    scanf("%lld%lld",&n,&k),k<<=30;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld%lld",&a[i],&b[i]);
        insert((a[i]<<30)|b[i]);
    }
    res=now.query(0);
    for(int i=30;i<=59;i++)
        for(int j=30;j<i;j++)
            if(v[i]>>j&1) v[i]^=v[j];
    for(int i=30;i<=59;i++) flg|=v[i]&&k>=(v[i]&~all);
    if(!flg) printf("-1"),exit(0);
    int tmp=0;
    for(int i=30;i<=59;i++) if(k>>i&1) tmp|=v[i];
    if(k==(tmp&~all)) res=max(res,tmp&all);
    for(int i=59;i>=30;i--)
    {
        if(!(k>>i&1)) continue;
        int cur=0;
        for(int j=i+1;j<=59;j++) if(k>>j&1) cur^=v[j];
        if((cur&~all)>k) break;
        auto tmp=now;
        for(int j=30;j<=i-1;j++) tmp.insert(v[j]&all);
        res=max(res,tmp.query(cur&all));
    }
    printf("%lld\n",res);
    return 0;
}

posted on 2022-06-02 19:43  peiwenjun  阅读(10)  评论(0)    收藏  举报

导航