洛谷-P14386 [JOISC 2017] 故障机器 题解

解题思路

注意到若 \(A_i=1\) 那么 \(A_i\) 一定没有被损坏。我们尝试通过把信息编码进 \(1\) 的位置。

显然无法朴素地对 \(X\) 拆位。考虑对每个位置设置一个 \([0,2^{60})\) 的权值 \(w_i\)。然后找到一种方案使得 \(X\) 等于所有 \(1\) 的位置设置的权值的异或和,即

\[X=\bigoplus_{i\in T}w_i \]

其中 \(T=\{i|S_i=1\}\)。

我们可以通过设置固定随机种子的技巧让双方事先约定好 \(\{w_i\}_{i=0}^{N-1}\)。然后直接异或线性基解决即可。

可以证明,这种方式的成功率足够大。

恰有 \(110\) 个可用位置,每个权值视为一个 \(60\) 维布尔向量,即

\[w_i\in\mathbb Z_2^{60} \]

且向量的取值互相独立。

任意向量 \(x\) 和 \(y\) 的点积为 \(0\) 的概率为 \(\frac{1}{2}\)。这里点积定义为 \(\operatorname{popcount}(x \operatorname{and} y) \bmod 2\)。

因此存在一个 \(x\) 使其和所有 \(w_i\) 的点积都为 \(0\) 的概率不超过

\[2^{60}\times 2^{-110}=2^{-50} \]

即这 \(110\) 个向量恰能张成整个 \(\mathbb Z_2^{60}\) 空间的概率至少为

\[1-2^{-50} \]

共 \(1000\) 组测试,总成功概率不低于

\[(1-2^{-50})^{1000}\approx 1-10^{-12} \]

Code

Anna.cpp

#include "Annalib.h"
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i(a);i<b;++i)
#define per(i,a,b) for(int i(a);i>b;--i)
#define rept(i,a,b) for(int i(a);i<=b;++i)
#define pert(i,a,b) for(int i(a);i>=b;--i)
using namespace std;
namespace{
    constexpr int SEED=0,N=150,M=60;
    ll a[N],b[M];
    bitset<N> msk[M];
    // b[i]:线性基中最高位是i的数,没有则为0
    // msk[i]:线性基中最高位是i的数是哪些a的元素异或起来得到的
    bool f[N],flag;
    void init(){
        if(!flag){
            mt19937 gen(SEED);
            uniform_int_distribution<ll> dist(0,(1ll<<60)-1);
            rep(i,0,N) a[i]=dist(gen);
            flag=true;
        }
        fill(f,f+N,false);
        fill(b,b+M,0);
        rep(i,0,M) msk[i].reset();
    }
}
void Anna(int n,ll x,int k,int p[]){
    init();
    rep(i,0,k) f[p[i]]=true;
    rep(i,0,n) if(!f[i]){
        // 将a[i]插入线性基
        ll tb=a[i];
        bitset<N> tmsk;
        tmsk.set(i);
        pert(j,M-1,0) if(tb>>j&1){
            if(b[j]){
                tb^=b[j];
                tmsk^=msk[j];
            }else{
                b[j]=tb;
                msk[j]=tmsk;
                break;
            }
        }
    }
    // 判断x能否被线性基中的数表示
    bitset<N> xmsk;
    pert(j,M-1,0) if(x>>j&1){
        x^=b[j];
        xmsk^=msk[j];
    }
    rep(i,0,n) Set(i,xmsk[i]);
}

Bruno.cpp

#include "Brunolib.h"
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i(a);i<b;++i)
#define per(i,a,b) for(int i(a);i>b;--i)
#define rept(i,a,b) for(int i(a);i<=b;++i)
#define pert(i,a,b) for(int i(a);i>=b;--i)
using namespace std;
namespace{
    constexpr int SEED=0,N=150,M=60;
    ll a[N];
    bool flag;
    void init(){
        if(!flag){
            mt19937 gen(SEED);
            uniform_int_distribution<ll> dist(0,(1ll<<60)-1);
            rep(i,0,N) a[i]=dist(gen);
            flag=true;
        }
    }
}
ll Bruno(int n,int f[]){
    init();
    ll ans=0;
    rep(i,0,n) if(f[i]) ans^=a[i];
    return ans;
}
posted @ 2026-11-15 15:33  xiaoniu142857  阅读(6)  评论(0)    收藏  举报