[NOI2021] 量子通信

\(\text{Problem}:\)[NOI2021] 量子通信

\(\text{Solution}:\)

首先有一个 \(O(\frac{256nm}{\omega})\) 的暴力做法。然后考虑如何将信息分段来优化匹配过程。

注意到 \(k_{max}=15\),而 \(256=16\times 16\),这提示可以将字典中的每个单词分为 \(16\) 段。不难发现,对于查询串,一定有某一段和某个单词是匹配的。枚举这一段和与其匹配的单词,暴力遍历其他段判断答案是否合法(不匹配的位置小于等于 \(k\))即可。

注意优化时间常数(如将 \(\text{vector}\) 换成数组等)。

\(\text{Code}:\)

#include <bits/stdc++.h>
//#pragma GCC optimize(3)
//#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
#define vi vector<int>
#define vpi vector<pair<int,int>>
using namespace std;
inline int read()
{
	int s=0, w=1; ri char ch=getchar();
	while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); }
	while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar();
	return s*w;
} char inp[256];
typedef unsigned long long ull;
const int MN = 400000;
//bool s[MN+1][256];
int wb[MN+1][16];
inline 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;
}
int g[16][65536][25],tot[16][65536]; int cnt[65536];
inline void gen(int n, ull a1, ull a2) {
    for (ri int i = 1; i <= n; i++)
    {
    	int now=0;
        for (ri int j = 0; j < 256; j++)
        {
            //s[i][j] = (myRand(a1, a2) & (1ull << 32)) ? 1 : 0;
            int du=j/16, pc=j%16;
            if((myRand(a1, a2) & (1ull << 32))) now|=(1<<pc);
            if(j%16==15) g[du][now][++tot[du][now]]=i, wb[i][du]=now, now=0;
        }
    }
}
int n,m,lsta; ull a1,a2;
int val[16];
inline void Read()
{
	scanf("%s",inp);
	for(ri int i=0;i<16;i++)
	{
		int now=0;
		for(ri int j=0;j<4;j++)
		{
			int w,k=i*4+j;
			if(inp[k]>='0'&&inp[k]<='9') w=inp[k]-'0';
			else w=inp[k]-'A'+10;
			if(lsta) w^=15;
			for(ri int x=0;x<4;x++) if((w>>x)&1) now|=(1<<(j*4+3-x));
		}
		val[i]=now;
	}
}
signed 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(ri int i=0;i<65536;i++) cnt[i]=cnt[i>>1]+(i&1);
	for(ri int i=1;i<=m;i++)
	{
		Read(); int lim=read();
		int flg=0;
		for(ri int j=0;j<16;j++)
		{
			for(ri int k=1;k<=tot[j][val[j]];k++)
			{
				int id=g[j][val[j]][k];
				int res=0;
				for(ri int x=0;x<16;x++)
				{
					if(j==x) continue;
					res+=cnt[val[x]^wb[id][x]];
					if(res>lim) break;
				}
				if(res<=lim) { flg=1; break; }
			}
			if(flg) break;
		}
		printf("%d\n",lsta=flg);
	}
	return 0;
}
posted @ 2021-07-29 09:01  zkdxl  阅读(91)  评论(0编辑  收藏  举报