【UNR #7】比特迷宫

发布时间 2023-07-18 20:41:37作者: Thunder_S

Description

小青鱼来到了重 (zhòng) 庆市的一个迷宫,名为比特迷宫。听说只有最聪明的人才能从里面走出。

这个迷宫看似容易,但在小青鱼即将走出迷宫的时候,却被 \(n=2^k\) 个比特机器人拦住了去路。这些机器人从左到右显示着 \(a_{0,}, a_1, \cdots, a_{n-1} ,\) 表示一个 \(n-1\) 次的多项式 \(P(x)=a_0+a_1 x+\cdots+a_{n-1} x^{n-1}\) 的系数。比特机器人喜欢比特,所以每个系数只可能是 0 或者 1 。

小青鱼是无法单独修改某一个比特机器人显示的系数的,但是这个迷宫提供了一个批量修改的操作: 输入两个非负整数 \(a, b(a+b \leq n-1)\) ,显示第 \(i\) 次项系数的比特机器人会算出 \(x^a(1+x)^b\) 的第 \(i\) 次项系数 \(c_i\) ,然后将自己显示的值 \(a_i\) 修改为 \(\left(a_i+c_i\right) \bmod 2\) ,其中 \(\bmod 2\) 表示对 2 取模。

整体来看,这个操作的作用是将这个多项式 \(P(x)\)\(\bmod 2\) 意义上加上 \(x^a(1+x)^b\)

由于这个迷宫是困难模式,小青鱼只能操作不超过 \(T\) 次。当多项式弯为 0 的时候,也即所有比特机器人都显示 0 的时候,他就通关了。

小青鱼左思右想没有想到通关的方式,于是他找到了你来帮忙。

\(1\le k\le 20\)\(a_i\in\{0,1\}\)\(1.6\times 10^5\le T\le 10^6\)

Solution

首先,\(\dbinom{n}{m}\mod 2=[n\&m==m]\)。也就是说,当 \(m\subset n\) 时,\(\dbinom{n}{m}\) 是奇数,反之为偶数。

现在我们需要步数最小,根据贪心的思路,每次操作都要清掉尽可能多的 1。

\(A\) 分成一段段区间(\(1\sim len,2\sim len+1,\cdots\)),每次处理其中一个,找到一个合适的 \(a,b\) 清掉尽可能多的 1。

\(A'_i\) 表示当 \(A_i=1\) 时,\(A'_i=1\);当 \(A_i=0\) 时,\(A'_i =-1\)。设 \(S_i=\sum_{j\subset i} A'_j\),表示在这个区间中,使用 \((1+x)^i\) 会造成多少贡献。1 变成 0 是正贡献,0 变成 1 是负贡献。每次更新 \(A_i\) 后都重新求一遍 \(S_i\),然后选择最大的 \(S_i\) 去更新 \(A_i\)

\(S_i\) 的计算是经典的 FWT,直接套版子就好。

最后,这个算法的正确性并没有被严格的保证,存在被构造数据导致 WA 的情况,对此我们可以在一开始随机的进行几次操作,打乱原本的序列,减小被卡的可能性。

Code

#include<cstdio>
#include<stdlib.h>
#include<time.h>
#include<algorithm>
#define N 1100000
#define len 1024
using namespace std;
int n,T,mx,ans1,a[N],ans[N][2],b[2000];
void FWT()
{
    for (int k=1;k<len;k<<=1)
        for (int i=0;i<len;i+=(k<<1))
            for (int j=0;j<k;++j)
                b[i+j+k]+=b[i+j];
}
int main()
{
    srand(time(NULL));
    scanf("%d%d%d",&n,&T,&mx);
    for (int i=1;i<=n;++i)
        scanf("%d",&a[i]);
    for (int t=0;t<=n/500;++t)
    {
        ++ans1;
        ans[ans1][0]=t*500;
        ans[ans1][1]=rand()%(n-ans[ans1][0]-1)+1;
        for (int i=0;i<=ans[ans1][1];++i)
            if ((i&ans[ans1][1])==i) a[ans[ans1][0]+i+1]^=1;
    }
    for (int i=1;i<=n-len+1;++i)
        if (a[i])
        {
            for (int j=0;j<len;++j)
                b[j]=a[i+j]?1:-1;
            FWT();
            int mx=0;
            for (int j=1;j<len;++j)
                if (b[j]>b[mx]) mx=j;
            ++ans1;
            ans[ans1][0]=i-1;
            ans[ans1][1]=mx;
            for (int j=0;j<=mx;++j)
                if ((mx|j)==mx) a[i+j]^=1;
        }
    for (int i=max(n-len,1);i<=n;++i)
    {
        if (a[i])
        {
            ans[++ans1][0]=i-1;
            ans[ans1][1]=0;
        }
    }
    printf("%d\n",ans1);
    for (int i=1;i<=ans1;++i)
        printf("%d %d\n",ans[i][0],ans[i][1]);
    return 0;
}