Vi 和 Powder 的加密通话
前言
通信题,好玩!但是做不来,坏耶~
题目
Vi 和 Powder 小时候喜欢加密通话。她们要向对方传递的信息是一个长度为 \(n\) 的 01 串。
在这道题中,我们假设 Vi 是传递信息者,Powder 是接收信息者。
她们通过大人们用过的废弃的信息条来模拟信息干扰,具体的,这张信息条长度为 \(m\),有 \(k\) 个位置已经确定为 \(0\) 或 \(1\),但两人事先都不知道这张信息条到底长什么样。
她们能做的就是事先约定好加密与解密的方法,之后 Vi 会得到一条长度为 \(n\) 的 01 串作为需要传递的信息,以及一条长度为 \(m\) 的信息条,上面有 \(k\) 个位置已经被 0 或 1 占用。
当 Vi 将信息加密后,她可以将加密过后的信息写到信息条上,然后交给 Powder,由 Powder 独立解密出原文即算成功。
Vi 和 Powder 当然知道怎么做,那么你呢?
你需要实现以下两个函数来满足通信需求:
void encoder(int n, int m, int k, const char* a, const char* b, char* ans);
void decoder(int n, int m, const char* a, char* ans);
\(1\le n,k\le 1000;n+k+50\le m\le 2050.\)
有部分分,但我懒得写了。
讲解
我会做的就是把原串暴力放很多遍,在 \(k\) 很小的时候根据鸽巢原理必定有比较多的原串出现,之后选出现最多的那个就行了,这个可以过 \(25pts\),但是由于未知原因直接 20ms+ TLE.
接下来我们直接讲正解,由于笔者数学很差,所以我会尽量从 OI 的角度来解释接下来的操作。
首先我们随机生成 \(m\) 个长度为 \(n\) 的 01 串,然后我们将有限制 \(k\) 个位置对应的 01 串剔除,此时我们至少剩下了 \(n+50\) 个 01 串,将他们全部塞进一个线性基,把线性基塞满的概率大概是 \(1-2^{-50}\),几乎是必定成功的。
我们将随机数种子共享,于是我们可以得到相同的随机数表(不是线性基)。
得到线性基之后,我们显然就可以用线性基里面的长度为 \(n\) 的 01 串来表示我们的原信息,而由于我们是将有限制的 \(k\) 个位置剔除了的,所以我们在信息条上传递的信息就可以是是否需要异或随机数表上的 01 串。
假设我们用 0 表示不异或,1 表示异或,我们其实就可以通过这个方法解密出原串了。
吗?
注意那有限制的 \(k\) 个位置也可能存在 \(1\),所以我们将那些位置上的串在线性基中提前异或一次,之后传过去再异或就可以抵消影响了。
用bitset
可以优化这个过程,时间复杂度 \(O(\frac{n^3}{\omega})\)。
代码
//12252024832524
#include "password.h"
#include
using namespace std;
const int MAXN = 2055;
bitset s[MAXN],ID[MAXN],rd[MAXN];//线性基;用到的ID;随机种子
void ins(bitset B,int I,int len)
{
bitset idid; idid[I] = 1;
for(int i = len-1;i >= 0;-- i)
if(B[i] & 1)
{
if(!s[i][i]) {s[i] = B,ID[i] = idid;break;}
else B ^= s[i],idid ^= ID[i];
}
}
bitset Query(bitset B,int len)
{
bitset ret;
for(int i = len-1;i >= 0;-- i)
if(B[i] & 1) B ^= s[i],ret ^= ID[i];
return ret;
}
void encoder(int n, int m, int k, const char* a, const char* b, char* ans)
{
mt19937 e('X'+'J'+'X'+'Y'+'Y'+'D'+'S');
for(int i = 0;i < m;++ i)
for(int j = 0;j < n;++ j)
rd[i][j] = e() & 1;
bitset now;
for(int i = 0;i < n;++ i) now[i] = a[i] - '0';
for(int i = 0;i < m;++ i)
if(b[i] == '?') ins(rd[i],i,n);
bitset ps = Query(now,n);
for(int i = 0;i < m;++ i) if(b[i] == '1') ps ^= Query(rd[i],n);
for(int i = 0;i < m;++ i)
{
if(b[i] == '?') ans[i] = (ps[i]+'0');
else ans[i] = b[i];
}
}
void decoder(int n, int m, const char* a, char* ans)
{
mt19937 e('X'+'J'+'X'+'Y'+'Y'+'D'+'S');
for(int i = 0;i < m;++ i)
for(int j = 0;j < n;++ j)
rd[i][j] = e() & 1;
bitset now;
for(int i = 0;i < m;++ i) if(a[i] == '1') now ^= rd[i];
for(int i = 0;i < n;++ i) ans[i] = now[i]+'0';
}