LOJ #3535. 「NOI2021」量子通信
题目叙述
一本词典里有 \(n\) 个单词。每个单词长度 256 。词典随机生成。
每次给出一个长度为256的单词,询问是否存在一个单词之和他相差不超过 \(k\) 个字符。
单词都由01字母组成。强制在线,每次得到的单词需要异或上上一个的那个01答案。
\(k\le 15,n\le 4\times 10^5\)
题解
发现 \(k\le 15\) ,考虑每 16 位分一块。
那么就会有至少一块完全相同。而字典是随机生成的,因此一块完全相同的期望个数是 \(\frac{n}{2^16}\) 的,比较小。
因此直接用一个链表存储第 \(i\) 块恰好是 \(j\) 的所有数,然后每次询问直接枚举一块有 16 种,然后再有 \(\frac{n}{2^16}\) 个数需要比较相同不同,比较直接分 16 块,每次异或完了之后算 popcount 。
总结
- 对恰好少 1 这种玩意要敏感。
- 另外对于这种鬼畜题需要时刻记得按位分块。
代码
#include <cstdio>
#include <iostream>
#define macro_expand(x) #x
#define print_macro(x) printf("%s\n",macro_expand(x))
#define FOR(i,l,r) for(int i=(l),i##ADJK=(r);i<=i##ADJK;++i)
#define ROF(i,r,l) for(int i=(r),i##ADJK=(l);i>=i##ADJK;--i)
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int MN = 400000+5;
bool s[MN+1][256];
ull myRand(ull &k1, ull &k2) {
ull k3 = k1, k4 = k2;
k1 = k4;
k3 ^= (k3 << 23);
k2 = k3 ^ k4 ^ (k3 >> 17) ^ (k4 >> 26);
return k2 + k4;
}
void gen(int n, ull a1, ull a2) {
for (int i = 1; i <= n; i++)
for (int j = 0; j < 256; j++)
s[i][j] = (myRand(a1, a2) & (1ull << 32)) ? 1 : 0;
}
int N,M;
ull a1,a2;
int head[16][65536];
struct Node{
int val,nxt;
Node():val(0),nxt(0){}
Node(int _val,int _nxt):val(_val),nxt(_nxt){}
}thing[16*MN];
int tot;
void add(int i,int j,int val){
thing[++tot]=Node(val,head[i][j]);
head[i][j]=tot;
}
int book[MN][16];
int popc[65536];
int main(){
freopen("qi.in","r",stdin);
freopen("qi.out","w",stdout);
scanf("%d%d%llu%llu",&N,&M,&a1,&a2);
gen(N,a1,a2);
FOR(i,1,N){
for(int j=0;j<16;++j){
int post=0;
FOR(k,j*16,j*16+15)post=post*2+s[i][k];
book[i][j]=post;
add(j,post,i);
}
}
FOR(i,1,65535)popc[i]=(i&1)+popc[i>>1];
int lastans=0;
while(M--){
static char str[100];
static int qry[16];
scanf("%s",str);
for(int i=0;i<64;i+=4){
int post=0;
FOR(j,i,i+3){
int num=(str[j]<='9'&&str[j]>='0')?(str[j]-'0'):(str[j]-'A'+10);
post=post*16+num;
}
if(lastans)qry[i/4]=post^65535;
else qry[i/4]=post;
}
int K=0;scanf("%d",&K);
bool can=0;
FOR(i,0,15){
for(int p=head[i][qry[i]];p;p=thing[p].nxt){
int id=thing[p].val,diff=0;
bool fu=1;
FOR(j,0,15){
diff+=popc[qry[j]^book[id][j]];
if(diff>K){fu=0;break;}
}
if(fu){can=1;break;}
}
if(can)break;
}
if(can)printf("1\n"),lastans=1;
else printf("0\n"),lastans=0;
}
fclose(stdin);
fclose(stdout);
return 0;
}

浙公网安备 33010602011771号