1. 程式人生 > >#UOJ 384 luoguP4424 HNOI2018 尋寶遊戲 思維題

#UOJ 384 luoguP4424 HNOI2018 尋寶遊戲 思維題

題意

  • 給你nn0101串,qq次詢問,每次詢問一個0101串,問在每個0101串前新增&或|,有多少種方案使得最後的結果為詢問的串,對1e9+71e9 + 7取模n<=1e3,q<=1e3n <= 1e3, q <= 1e3

這個題看了好久題解才會做… 做題要心靜啊

首先考慮按位處理 對於其中的每一位我們提出來看

首先我們觀察一下位運算的一些性質

1and0=0,0and0=01 \ and\ 0 = 0, 0 \ and \ 0 = 0

這說明如果and0and \ 0的話這位的值就和前面的沒關係 o

r1or\ 1同理

1and1=1,0and1=01\ and\ 1 = 1, 0\ and\ 1 = 0

這說明如果and1and\ 1的話這位的值就和當前位沒關係 or0or\ 0同理

那麼我們把運算子變成0101序列,or=0,and=1or = 0, and =1

那麼如果某一位上的值是11的話

那麼它最後一個or1or\ 1一定在最後一個and0and\ 0前面

oror00, andand11轉化一下就是

運算子序列的字典序小於當前序列

同理那一位是00就是字典序大於等於當前序列

那麼這個題目就轉化成了一個求字串字典序的題

直接用stdstringstd\ string就好了 有些細節要注意

注意下詢問全00序列,因為我們的區間L,RL, R是前閉後開

但是對於全00序列來說全部用11操作也是合法的

然後就做完了 複雜度O(q(n+m)logn)O(q(n + m)logn)

Codes

#include<bits/stdc++.h>

using namespace std;

const int mod = 1e9 + 7;
const int N = 5000 + 10;

int n, m, q, _pow2[N], flag;

string now[N]
, a[N], Que, L, R, tmp; void Solve() { for(int i = 0; i < m; ++ i) now[i] = tmp; for(int i = n; i >= 1; -- i) for(int j = 0; j < m; ++ j) now[j][n - i] = a[i][j]; } int Rank(string S) { int res = 0; for(int i = 0; i < n; ++ i) (res += _pow2[n - i - 1] * (S[i] - '0')) %= mod; return res; } int main() { #ifndef ONLINE_JUDGE freopen("4424.in", "r", stdin); freopen("4424.out", "w", stdout); #endif scanf("%d%d%d", &n, &m, &q); for(int i = 1; i <= n; ++ i) cin >> a[i]; _pow2[0] = 1; tmp = a[1]; for(int i = 1; i <= N - 5; ++ i) _pow2[i] = _pow2[i - 1] * 2 % mod; if(n > m) for(int i = 1; i <= n - m; ++ i) tmp += "1"; if(n < m) for(int i = 1; i <= m - n; ++ i) tmp.pop_back(); L = R = tmp; Solve(); for(int cas = 1; cas <= q; ++ cas, flag = 0) { cin >> Que; for(int i = 0; i < n; ++ i) L[i] = '0', R[i] = '1'; for(int i = 0; i < m; ++ i) if(Que[i] == '1') flag = 1; for(int i = 0; i < m; ++ i) if(Que[i] - '0') R = min(R, now[i]); else L = max(L, now[i]); if(L >= R) {puts("0"); continue;} printf("%d\n", (Rank(R) - Rank(L) + 1 - flag + mod) % mod); } return 0; }