bzoj 5285 [HNOI2018] 寻宝游戏

bzoj 5285 [HNOI2018] 寻宝游戏

Solution

这题太可怕了

想不到

按位考虑

对于当某一位,\(\& 1,\| 0\) 这两种操作对当前数完全没有影响,我们只要找到倒着第一次的 \(\&0,\| 1\),或者根本就没有这样的玩意

我们如果想让这一位是 \(1\),那么就得让倒着第一次的 \(\& 0,\| 1\) 一定是 \(\| 1\),而且必须出现(否则就是 \(0\) 做了一堆不变的操作,最后还是 \(0\)

我们考虑把操作按照倒序写成 \(01\) 串,就是 \(\|\) 写成 \(0\)\(\&\) 写成 \(1\)

假设当前考虑第 \(i\) 位,我们令 \(s =\overline {s_{n,i}s_{n-1,i}s_{n-2,i}\dots s_{1,i}}\)(是一个 \(01\) 串),发现当且仅当操作序列串字典序小于等于 \(s\) 的时候,这一位的结果最后是 \(1\),字典序大于 \(s\) 的时候,这一位最后等于 \(0\)

那么对于当前询问,变成了对于每一位都有一个操作序列小于等于一个串或者大于一个串的限制

统计一下上下界就好了

具体实现见代码

Code

// Copyright lzt
#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<cmath>
#include<iostream>
#include<queue>
#include<string>
#include<ctime>
using namespace std;
typedef long long ll;
typedef std::pair<int, int> pii;
typedef long double ld;
typedef unsigned long long ull;
typedef std::pair<long long, long long> pll;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define rep(i, j, k)  for (register int i = (int)(j); i <= (int)(k); i++)
#define rrep(i, j, k) for (register int i = (int)(j); i >= (int)(k); i--)
#define Debug(...) fprintf(stderr, __VA_ARGS__)
 
inline ll read() {
  ll x = 0, f = 1;
  char ch = getchar();
  while (ch < '0' || ch > '9') {
    if (ch == '-') f = -1;
    ch = getchar();
  }
  while (ch <= '9' && ch >= '0') {
    x = 10 * x + ch - '0';
    ch = getchar();
  }
  return x * f;
}
 
const int maxn = 5050;
const int mod = 1e9 + 7;
int n, m, q;
char str[maxn];
int val[maxn][1010], num[maxn], rnk[maxn];
 
bool cmp(int x, int y) {
  rrep(i, n, 1) 
    if (val[x][i] != val[y][i])
      return val[x][i] > val[y][i];
  return x < y;
}
 
void work() {
  n = read(), m = read(), q = read();
  rep(i, 1, n) {
    scanf("%s", str + 1);
    rep(j, 1, m) val[j][i] = str[j] - '0';
  }
  int full = 1;
  rep(i, 1, n) full = full * 2 % mod;
  rep(i, 1, m) rrep(j, n, 1) {
    num[i] = (num[i] * 2 + val[i][j]) % mod;
  }
  rep(i, 1, m) rnk[i] = i;
  sort(rnk + 1, rnk + m + 1, cmp);
  while (q--) {
    scanf("%s", str + 1);
    bool f = 0, flag = 0;
    rep(i, 1, m) {
      if (str[rnk[i]] == '0') f = 1;
      else if (f) {
        puts("0");
        flag = 1;
      }
      if (flag) break;
    }
    if (flag) continue;
    rep(i, 1, m) {
      if (str[rnk[i]] == '0') {
        if (i == 1) printf("%d\n", (full - num[rnk[i]] + mod) % mod);
        else printf("%d\n", (num[rnk[i - 1]] - num[rnk[i]] + mod) % mod);
        flag = 1;
      }
      if (flag) break;
    }
    if (!flag) printf("%d\n", num[rnk[m]]);
  }
}
 
int main() {
  #ifdef LZT
    freopen("in", "r", stdin);
  #endif
 
  work();
 
  #ifdef LZT
    Debug("My Time: %.3lfms\n", (double)clock() / CLOCKS_PER_SEC);
  #endif
}

Review

神仙题

对于有关位运算和 \(01\) 串的题,都可以尝试按位考虑

至于这道题怎么想到字典序的。。。只有出题人能想到系列。。。

猜你喜欢

转载自www.cnblogs.com/wawawa8/p/10162869.html