Codeforces #251D: Two Sets 题解

考虑贪心
按照位从高到低考虑,假设当前正在考虑第i位
如果第i位是1的数有奇数个,那么无论怎么分配,总会有一个人拿到奇数个,另一个人拿到偶数个,所以X1和X2的第i位一定有一个是1,另一个是0,无论怎么分配都是不会影响X1+X2的
如果第i位是1的数有偶数个,那么要么两个人都拿偶数个,这时X1和X2的第i位都是0,要么两个人都拿奇数个,这时X1和X2的第i位都是1,后者的X1+X2肯定是大于前者的,由二进制的性质可得,只要能满足后者,就一定应该满足
考虑怎样描述这样的一个限制;我们设第i位是1的数的下标是 a1 a 1 , a2 a 2 ak a k ,对每一个位置记录一个 Xi X i , Xi=1 X i = 1 表示这个数分给第一个人, Xi=0 X i = 0 表示分给第二个人,这样我们的限制条件可以写成这样: xa1xa2...xak=1 x a 1 ⨁ x a 2 ⨁ . . . ⨁ x a k = 1
这样的一些异或方程组可以用高斯消元搞,每次添一个方程,检查是否还有解
注意添加一个新方程后,不要把整个方程组解一遍,只要对新方程消元就好了
用bitset压位,总复杂度 O(n64) O ( n ∗ 64 )

*感觉学完线性基以后,理解高斯消元更加深刻。感觉这两个东西其实很像
*判断方程无解的方法是:消元之后,如果有一个方程,左边全是0,右边不为0,则无解

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>
#include <utility>
#include <cctype>
#include <algorithm>
#include <bitset>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <deque>
#include <stack>
#include <cmath>
#define LL long long
#define LB long double
#define x first
#define y second
#define Pair pair<int,int>
#define pb push_back
#define pf push_front
#define mp make_pair
#define LOWBIT(x) x & (-x)
using namespace std;

const int MOD=1e9+7;
const LL LINF=2e16;
const int INF=1e9;
const int magic=348;
const double eps=1e-10;
const double pi=3.14159265;

inline int getint()
{
    char ch;int res;bool f;
    while (!isdigit(ch=getchar()) && ch!='-') {}
    if (ch=='-') f=false,res=0; else f=true,res=ch-'0';
    while (isdigit(ch=getchar())) res=res*10+ch-'0';
    return f?res:-res;
}

inline LL getLL()
{
    char ch;LL res;bool f;
    while (!isdigit(ch=getchar()) && ch!='-') {}
    if (ch=='-') f=false,res=0; else f=true,res=ch-'0';
    while (isdigit(ch=getchar())) res=res*10+ch-'0';
    return f?res:-res;
}

typedef bitset<100048> bs;

struct equation
{
    bs b;int res;
    inline void init() {b=0;res=0;}
}cur;
map<int,equation> b;
bool exist[100048];

int n;
LL a[100048];
int maxbit;
int ans[148];
int sol[100048];

inline bool check()
{
    int bit,i;
    for (i=1;i<=n;i++)
        if (cur.b[i])
            if (exist[i]) cur.b^=b[i].b,cur.res^=b[i].res; else {b[i]=cur;exist[i]=true;return true;}
    if (cur.res) return false; else return true;
}

inline void find_solution()
{
    int i,j;
    for (i=n;i>=1;i--)
    {
        if (!exist[i]) {sol[i]=0;continue;}
        for (j=i+1;j<=n;j++)
            if (b[i].b[j]) b[i].res^=sol[j];
        sol[i]=b[i].res;
    }
}       

int main ()
{
    int i,bit;n=getint();
    for (i=1;i<=n;i++) a[i]=getLL();
    for (maxbit=63;maxbit;maxbit--)
    {
        bool f=false;
        for (i=1;i<=n;i++)
            if (a[i]&(1ll<<(maxbit-1))) {f=true;break;}
        if (f) break;
    }
    for (bit=maxbit;bit;bit--)
    {
        cur.init();
        for (i=1;i<=n;i++)
            if (a[i]&(1ll<<(bit-1))) cur.b[i]=1,cur.res++;
        if (cur.res%2==1) {ans[bit]=1;continue;}
        cur.res=1;
        if (check()) ans[bit]=2; else ans[bit]=0;
    }
    for (bit=maxbit;bit;bit--)
        if (ans[bit]==1)
        {
            cur.init();
            for (i=1;i<=n;i++)
                if (a[i]&(1ll<<(bit-1))) cur.b[i]=1;
            cur.res=0;
            check();
        }
    find_solution();
    for (i=1;i<=n;i++) printf("%d ",2-sol[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值