嘘~ 正在从服务器偷取页面 . . .

洛谷P4424 [HNOI/AHOI2018] 寻宝游戏 题解


洛谷P4424 [HNOI/AHOI2018] 寻宝游戏 题解

题目链接:P4424 [HNOI/AHOI2018] 寻宝游戏

题意

某大学每年都会有一次 Mystery Hunt 的活动,玩家需要根据设置的线索解谜,找到宝藏的位置,前一年获胜的队伍可以获得这一年出题的机会。

作为新生的你,对这个活动非常感兴趣。你每天都要从西向东经过教学楼一条很长的走廊,这条走廊是如此的长,以至于它被人戏称为 infinite corridor。一次,你经过这条走廊时注意到在走廊的墙壁上隐藏着 $n$ 个等长的二进制的数字,长度均为 $m$。你从西向东将这些数字记录了下来,形成一个含有 $n$ 个数的二进制数组 $a_1,a_2,…,a_n$。

很快,在最新的一期的 Voo Doo 杂志上,你发现了 $q$ 个长度也为 $m$ 的二进制数 $r_1,r_2,…,r_q$。

聪明的你很快发现了这些数字的含义。

保持数组 $a_1,a_2,…,a_n$ 的元素顺序不变,你可以在它们之间插入 $\land$(按位与运算)或者 $\lor$(按位或运算)。例如:$11011\land 00111=00011$,$11011\lor 00111=11111$。

你需要插入 $n$ 个运算符,相邻两个数之前恰好一个,在第一个数的左边还有一个。如果我们在第一个运算符的左边补入一个 0,这就形成了一个运算式,我们可以计算它的值。与往常一样,运算顺序是从左到右。有趣的是,出题人已经告诉你这个值的可能的集合—— Voo Doo 杂志里的那些二进制数 $r_1,r_2,…,r_q$,而解谜的方法,就是对 $r_1,r_2,…,r_q$ 中的每一个值 $r_i$,分别计算出有多少种方法填入这 $n$ 个计算符,使的这个运算式的值是 $r_i$。

然而,infinite corridor 真的很长,这意味着数据范围可能非常大。因此,答案也可能非常大,但是你发现由于谜题的特殊性,你只需要求答案模 $1000000007$ 的值。

输入格式

第一行三个数 $n,m,q$,含义如题所述。

接下来 $n$ 行,其中第 $i$ 行有一个长度为 $m$ 的二进制数,左边是最高位,表示 $a_i$。

接下来 $q$ 行,其中第 $i$ 行有一个长度为 $m$ 的二进制数,左边是最高位,表示 $r_i$。

输出格式

输出 $q$ 行,每行一个数,其中的 $i$ 行表示对于 $r_i$ 的答案。

数据范围

$1\leq n\leq 1000,1\leq m\leq 5000,1\leq q\leq 1000$。

依次考虑每一个数字 $i$ 的每一位 $j$ 。方便起见,我们定义最右侧的那一位是最高位。

注意到 $x\land 0=0$ 和 $x\lor 1=1$ 这两个操作,无论前面是什么结果,到这里就肯定是 $0$ 和 $1$ 。

注意到 $x \lor 0=x$ 和 $x \land 1=x$ 这两个操作,无论前面是什么结果,到这里都不会变。

要使得第 $j$ 位的运算结果是 $1$ ,因此最后一个 $x\lor 1$ 的位置要在 $x\land 0$ 的前面。

考虑记操作序列为 $\rm op$ ,其中 $\mathrm{op}(i):= [\textbf{option}=\, \land]$ 。也就是把 $\lor$ 记为 $0$ ,$\land$ 记为 $1$ 。

然后把第 $j$ 位的数提出来作为一个长度为 $n$ 的二进制数,记为序列 $x_j$ 。

那么根据刚才我们定义的序列,这个问题就转化为了 $x_j > \mathrm{op}$ 。例如

于是我们就把问题转化为了比大小。我们把 $x_j$ 按二进制数的值排序

然后每次判断 $x_j$ 和 $\rm op$ 的大小关系,如果有解可以得到 $x_l \le \mathrm{op} < x_r$ ,答案就是 $x_r - x_l$ 。

时间复杂度 $\mathcal{O}((n+q)m)$ ,排序可以用计数排序。

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
const int mod = 1e9 + 7;
void up(int &x, int y) { x < y ? x = y : 0; }
void down(int &x, int y) { x > y ? x = y : 0; }
void add(int &x, int y) { (x += y) >= mod ? x -= mod : 0; }
#define rep(i, a, b) for(int i = (a), i##END = (b); i <= i##END; i++)
#define Rep(i, a, b) for(int i = (a), i##END = (b); i >= i##END; i--)
#define N ((int)(5e3 + 15))

char s[N];
int pw2[N], a[N], b[N], t[N], cnt[2], v[N];
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    int n, m, q; cin >> n >> m >> q; pw2[1] = 1;
    rep(i, 2, n + 1) pw2[i] = pw2[i - 1] * 2 % mod;
    rep(i, 1, m) a[i] = i;
    rep(i, 1, n)
    {
        cin >> (s + 1); cnt[0] = 0; cnt[1] = m;
        rep(j, 1, m) { if(s[j] == '1') add(v[j], pw2[i]); else ++cnt[0]; }
        Rep(j, m, 1) { b[cnt[s[a[j]] - '0']--] = a[j]; } swap(a, b);
    }
    rep(i, 1, m) t[i] = v[a[i]] % mod;
    int mx, mn; t[m + 1] = pw2[n + 1];
    rep(l, 1, q)
    {
        cin >> (s + 1); mx = m + 1, mn = 0;
        Rep(i, m, 1) if(s[a[i]] == '0') { mn = i; break; }
        rep(i, 1, m) if(s[a[i]] == '1') { mx = i; break; }
        if(mx < mn) cout << "0\n";
        else { cout << (t[mx] + mod - t[mn]) % mod << '\n';}
    }
    return 0;
}

参考文献

[1] https://www.luogu.com.cn/article/n1y3maag


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录