1. 程式人生 > >[洛谷P4424][HNOI/AHOI2018]尋寶遊戲(bitset)

[洛谷P4424][HNOI/AHOI2018]尋寶遊戲(bitset)

typedef 成了 ott span long block radi 它的 ear

P4424 [HNOI/AHOI2018]尋寶遊戲


某大學每年都會有一次Mystery Hunt的活動,玩家需要根據設置的線索解謎,找到寶藏的位置,前一年獲勝的隊伍可以獲得這一年出題的機會。

作為新生的你,對這個活動非常感興趣。你每天都要從西向東經過教學樓一條很長的走廊,這條走廊是如此的長,以至於它被人戲稱為infinite corridor。一次,你經過這條走廊時註意到在走廊的墻壁上隱藏著nn 個等長的二進制的數字,長度均為mm 。你從西向東將這些數字記錄了下來,形成一個含有nn 個數的二進制數組a_1,a_2,...,a_na1?,a2?,...,an?

很快,在最新的一期的Voo Doo雜誌上,你發現了qq 個長度也為mm 的二進制數r_1,r_2,...,r_qr1?,r2?,...,rq?

聰明的你很快發現了這些數字的含義。

保持數組a_1,a_2,...,a_na1?,a2?,...,an? 的元素順序不變,你可以在它們之間插入∧ (按位與運算)或者∨ (按位或運算)。例如:11011∧00111=000111101100111=00011 ,11011∨00111=111111101100111=11111 。

你需要插入nn 個運算符,相鄰兩個數之前恰好一個,在第一個數的左邊還有一個。如果我們在第一個運算符的左邊補入一個0,這就形成了一個運算式,我們可以計算它的值。與往常一樣,運算順序是從左到右。有趣的是,出題人已經告訴你這個值的可能的集合——Voo Doo雜誌裏的那些二進制數r_1,r_2,...,r_qr1?,r2?,...,rq? ,而解謎的方法,就是對r_1,r_2,...,r_qr1?,r2?,...,rq? 中的每一個值r_iri? ,分別計算出有多少種方法填入這nn 個計算符,使的這個運算式的值是r_iri?

然而,infinite corridor真的很長,這意味著數據範圍可能非常大。因此,答案也可能非常大,但是你發現由於謎題的特殊性,你只需要求答案模1000000007的值。

輸入輸出格式


輸入格式:

第一行三個數nn ,mm ,qq ,含義如題所述。

接下來nn 行,其中第ii 行有一個長度為mm 的二進制數,左邊是最高位,表示a_iai?

接下來qq 行,其中第ii 行有一個長度為mm 的二進制數,左邊是最高位,表示r_iri?

輸出格式:

輸出qq 行,每行一個數,其中的ii 行表示對於r_iri? 的答案。

輸入輸出樣例


輸入樣例#1:
5 5 1
01110
11011
10000
01010
00100
00100

輸出樣例#1:
6

輸入樣例#2:

10 10 3
0100011011
0110100101
1100010100
0111000110
1100011110
0001110100
0001101110
0110100001
1110001010
0010011101
0110011111
1101001010
0010001001

輸出樣例#2:

69
0
5

說明


對於 10% 的數據,n \le 20, m \le 30, q = 1n20,m30,q=1

對於另外 20 的數據,n \le 1000, m \le 16n1000,m16

對於另外 40 的數據,n \le 500, m \le 1000n500,m1000

對於全部的數據1≤n≤1000,1≤m≤5000,1≤q≤10001n1000,1m5000,1q1000

分析:


倒著來推,每次枚舉每個位置是or 還是 and。設每個串為S[i],設每次查詢的串為G

然後有個表 :(設當前枚舉到第i個字符串的第j位,設F[i][j]表示(1 ~ i - 1)操作後第j位為0/1)

————————————

F[i - 1][j] S[i][j] G[j] type

0 / 1 0 0 and

0 1 0 and

1 1 1 and

-1 0 1 and

0 / 1 1 1 or

0 0 0 or

1 0 1 or

-1 1 0 or

————————————

-1表示(1~i - 1)的操作怎麽枚舉都不可能滿足,0/1表示(1 ~ i - 1)的操作隨便枚舉都可以滿足。

這樣我們倒著2^n次方枚舉所有填的可能,當出現-1就剪枝,當全部位都為0/1就可以直接加上2^(i - 1),並剪枝。

這樣剪下來,每一層狀態數最多為2,是不是很神奇。。

然後復雜度就變成了O(nq * (位運算代價))。

使用bitset(要手寫),把位運算代價優化成5000 / 64 = 78

總復雜度O(78nq)

AC代碼:


# include <iostream>
# include <cstdio>
# include <cstring>
using namespace std;
typedef unsigned long long llu;
const int N = 1012;
const int mod = 1e9 + 7;
int block,n,m,Q;
struct bitset{
   llu p[80];
   void read()
   {
       memset(p,0llu,sizeof p);
       char i = getchar();
    while(!isdigit(i))i = getchar();
    for(int j = 0;j < m;j++,i = getchar())p[j / 64] |= (((llu)(i - 0)) << (j % 64));
   }
   void clear(){for(int i = 0;i <= block;i++)p[i] = 0;}
}s[N],f[N],nt,t1,t2,v1,v2,g,c1,c2,que[2][4];
int bac[N],ret,dt,cur,tot,ans;
void init()
{
    block = (m - 1) / 64;
    bac[0] = 1;for(int i = 1;i <= n;i++)bac[i] = (bac[i - 1] << 1) % mod;
    memset(nt.p,0llu,sizeof nt.p);
    for(int j = 0;j < m;j++)nt.p[j / 64] |= (1llu << (j % 64));
    for(int i = 1;i <= n;i++)
    {
        s[i].read();
        for(int j = 0;j <= block;j++)
        f[i].p[j] = s[i].p[j] ^ nt.p[j];
    }
}
int main()
{
  scanf("%d %d %d",&n,&m,&Q);
  init();
  while(Q--)
  {
      g.read();dt = ans = cur = 0;que[cur][++dt].clear();
      for(int i = n;i;i--)
      {
          tot = dt;cur ^= 1;dt = 0;
          for(int j = 1;j <= tot;j++)
          {
            bool f1 = true,f2 = true,l1 = true,l2 = true;
            for(int k = 0;k <= block;k++)
          {
           if(!f1 && !f2)break;
           if(que[cur ^ 1][j].p[k] == nt.p[k])
           {
                if(f1)t1.p[k] = nt.p[k];
                if(f2)t2.p[k] = nt.p[k];
                continue;
           }
           if(f1)v1.p[k] = s[i].p[k] & (s[i].p[k] ^ g.p[k]);
           if(f2)v2.p[k] = g.p[k] & (s[i].p[k] ^ g.p[k]);
           if(f1 && (v1.p[k] & que[cur ^ 1][j].p[k]) != v1.p[k])f1 = false;
           if(f2 && (v2.p[k] & que[cur ^ 1][j].p[k]) != v2.p[k])f2 = false;
           if(f1)t1.p[k] = que[cur ^ 1][j].p[k] | (s[i].p[k] ^ v1.p[k]);
           if(f2)t2.p[k] = que[cur ^ 1][j].p[k] | (f[i].p[k] ^ v2.p[k]);
           if(f1 && t1.p[k] != nt.p[k])l1 = false;
           if(f2 && t2.p[k] != nt.p[k])l2 = false;
          }
          if(f1)
          {
              if(l1)ans = (ans + bac[i - 1]) % mod;
              else que[cur][++dt] = t1;
          }
          if(f2)
          {
              if(l2)ans = (ans + bac[i - 1]) % mod;
              else que[cur][++dt] = t2;
          }
          }
      }
      for(int j = 1;j <= dt;j++)
      {
          bool f1 = true;
          for(int k = 0;k <= block;k++)
          {
              if((que[cur][j].p[k] ^ nt.p[k]) & g.p[k]){f1 = false;break;}
          }
          ans += f1;
      }
      printf("%d\n",ans);
  }
}

[洛谷P4424][HNOI/AHOI2018]尋寶遊戲(bitset)