zoukankan      html  css  js  c++  java
  • 【洛谷P7738】量子通信

    题目

    题目链接:https://www.luogu.com.cn/problem/P7738
    小 Z 正在自学量子计算机相关知识,最近他在研究量子通信章节,并遇到了一个有趣的问题。在该问题中,Alice 和 Bob 正在进行量子通信,它们的通信语言是一个大小为 (n) 的字典 (S),在该字典中,每一个单词 (s_i)(1 le i le n))都可以用一个 (oldsymbol{256}) 位的 (oldsymbol{01}) 来表示。在本题中 (s_i) 可以通过调用函数 gen 来生成,选手可以在题目目录下的 gen.cpp 中查看,该函数的参数 na1a2 将由输入数据给出。
    Alice 和 Bob 接下来要进行 (m) 次通信,每次通信由 Alice 向 Bob 传输恰好一个字典中的单词。然而,两人使用的通信信道并不可靠,会受到噪音的干扰。更具体地,对于第 (i) 次传输,记 Alice 传输的原单词为 (x_i),该 (01) 串会受噪音干扰而翻转最多 (oldsymbol{k_i}) 。换句话说,记 Bob 这次收到的 (01) 串为 (y_i),它与 (x_i) 相比,可能有最多 (k_i) 位是不同的,并且 (y_i) 可能不在字典 (S) 中出现。
    与此同时,Bob 得知坏人 Eve 也潜入了两人的通信信道,并准备干扰两人的通信。他的干扰方式是将 Bob 收到的 (01) 串变为任意的 (256)(01) 串,并且这个串可能不在字典 (S) 中出现。Eve 非常狡猾,他不一定会对每次通信都进行干扰。
    现在 Bob 找来了你帮忙,对于接下来的每次通信,你需要根据 Bob 最终收到的 (01) 串以及这次通信的噪音干扰阈值 (k_i)(0 le k_i le 15)),判断这次通信是否有可能没有受到 Eve 的干扰(即 Bob 收到的 (01) 串可以由字典中的某个单词翻转至多 (k_i) 位后得到)。本次通信如果有可能没受到 Eve 干扰,请你输出 (1),否则输出 (0)。Bob 很信任你的能力,所以你需要在线地回答结果,具体要求见输入格式
    为了降低读入用时, Bob 收到的串将用长度为 (oldsymbol{64}) (oldsymbol{16}) 进制串给出,(16) 进制串中包含数字字符 ( exttt{0} sim exttt{9}) 与大写英文字母 ( exttt{A} sim exttt{F}),其中字符 ( exttt{A} sim exttt{F}) 依次表示数值 (10 sim 15)
    (16) 进制串可以逐位转化为 (01) 串,例如:5 对应 0101A 对应 1010C 对应 1100
    (nleq 4 imes 10^5,mleq 1.2 imes 10^5,kleq 15)

    思路

    这题和 GDOI2017 Day2 T2 简直完全不一样呢。
    (256) 位的二进制数,而 (kleq 15)。也就是说,如果两个二进制串汉明距离不超过 (k),把这两个长度为 (256) 的串分成 (16) 个长度为 (16) 的串,至少会有一段完全相同。
    而这 (n) 个串是可以看作完全随机的。随机 (n) 个长度为 (16) 的二进制串,与给定二进制串一致的期望个数为 (frac{n}{2^{16}}< 7)
    我们把这 (n) 个串都分成 (16) 份,记 (nxt[i][j]) 表示上一个和串 (i) 的第 (j) 段完全相同的是哪一个串。
    然后对于每一次询问,暴力枚举每一段,再枚举这一段完全相同的串尝试匹配。期望下匹配的次数是 (6 imes 16=96) 次。每次直接 lowbit 判断两个二进制数不同的位数即可。
    时间复杂度 (O(256n+1440m))。这个上界特别松,并且数据随机。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef unsigned long long ull;
    
    const int N=400010;
    int n,m,k,cnt,lastans,vis[N],nxt[N][16],a[N][16],b[16],last[16][1<<16];
    bool s[N][256];
    ull a1,a2;
    char t[100];
    
    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;
    }
    
    void check(ull x)
    {
    	for (;x && cnt<=k;cnt++)
    		x-=x&(x^(x-1ULL));
    }
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	scanf("%llu%llu",&a1,&a2);
    	gen(n,a1,a2);
    	for (int i=1;i<=n;i++)
    	{
    		for (int j=0;j<256;j++)
    			a[i][j/16]=(a[i][j/16]<<1)|s[i][j];
    		for (int j=0;j<16;j++)
    			nxt[i][j]=last[j][a[i][j]],last[j][a[i][j]]=i;
    	}
    	while (m--)
    	{
    		scanf("%s%d",t,&k);
    		memset(b,0,sizeof(b));
    		for (int i=0,x;i<64;i++)
    		{
    			if (isdigit(t[i])) x=t[i]-48;
    				else x=t[i]-'A'+10;
    			if (lastans) x^=15;
    			b[i/4]=(b[i/4]<<4)|x;
    		}
    		cnt=114514;
    		for (int i=0;i<16 && cnt>k;i++)
    			for (int j=last[i][b[i]];j;j=nxt[j][i])
    				if (vis[j]!=m+1)
    				{
    					vis[j]=m+1; cnt=0;
    					for (int l=0;l<16 && cnt<=k;l++) check(a[j][l]^b[l]);
    					if (cnt<=k) break;
    				}
    		lastans=(cnt<=k);
    		putchar(lastans+48); putchar(10);
    	}
    	return 0;
    }
    
  • 相关阅读:
    MathType中如何快速输入空心字母
    如何用MathType编辑出积分符号
    史上“最骚”公式编辑器,你了解多少!
    几何画板中直角符号为什么不带阴影
    mssql 版本查询
    python爬取网站数据保存使用的方法
    sql 联合查询
    sql join
    增删改查
    发送json-简单的传参查询和简单的sql查询
  • 原文地址:https://www.cnblogs.com/stoorz/p/15071798.html
Copyright © 2011-2022 走看看