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;
}
posted @ 2022-07-19 16:39  YouthRhythm  阅读(32)  评论(0)    收藏  举报