[BZOJ5285][HNOI2018]尋寶遊戲-思維
尋寶遊戲
Description
某大學每年都會有一次Mystery Hunt的活動,玩家需要根據設定的線索解謎,找到寶藏的位置,前一年獲勝的隊伍可以獲得這一年出題的機會。
作為新生的你,對這個活動非常感興趣。你每天都要從西向東經過教學樓一條很長的走廊,這條走廊是如此的長,以至於它被人戲稱為infinite corridor。一次,你經過這條走廊時注意到在走廊的牆壁上隱藏著
很快,在最新的一期的Voo Doo雜誌上,你發現了
聰明的你很快發現了這些數字的含義。
保持陣列
你需要插入
然而,infinite corridor真的很長,這意味著資料範圍可能非常大。因此,答案也可能非常大,但是你發現由於謎題的特殊性,你只需要求答案模1000000007的值。
Input
第一行三個數
接下來
接下來
Output
輸出
Sample Input
5 3 0
1 2 3 4 5
3 5
5 0
1 4
Sample Output
5
7
6
7
HINT
考場上不會倒推做法,暴力還超時了,正解想到一半就想不動了(而且也不知道那是正解)……
咱是真的菜QAQ
思路:
首先很顯然需要按位考慮。
觀察每一位,可以發現,
若某一位的值為
考慮把所有運算子寫成一個01串,令所有|運算為
同樣考慮蔣所有數某一位的
可以發現,把最右端看成最高位,從高到低比較同為
於是可以發現,能使結果為
考慮對於每一位形成的01串排序。
對於每次詢問,首先觀察是否合法,即是否有合法的運算子集合對於詢問串的每一位均滿足要求。
即,對於詢問串,所有為
然後計算答案,可以發現是小於最小的為
於是就做完了~
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1009;
const int M=5009;
const int md=1e9+7;
int n,m,q;
int rk[M],sum[M],tmp[M],pows[N];
char s[M];
int main()
{
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=m;i++)
rk[i]=i;
pows[0]=1;
for(int i=1;i<=n;i++)
pows[i]=pows[i-1]*2ll%md;
for(int i=1;i<=n;i++)
{
int cnt[]={0,0};
scanf("%s",s+1);
for(int j=1;j<=m;j++)
{
sum[j]=(sum[j]+(ll)(s[j]-'0')*pows[i-1])%md;
cnt[s[j]-'0']++;
}
cnt[1]+=cnt[0];
for(int j=m;j;j--)
tmp[cnt[s[rk[j]]-'0']--]=rk[j];
swap(rk,tmp);
}
reverse(rk+1,rk+m+1);
for(int i=1;i<=q;i++)
{
scanf("%s",s+1);
int lst1=0,fst0=m+1;
for(int j=m;j>=1 && lst1==0;j--)
if(s[rk[j]]-'0')
lst1=j;
for(int j=1;j<=m && fst0==m+1;j++)
if(!(s[rk[j]]-'0'))
fst0=j;
if(lst1>fst0)
{
puts("0");
continue;
}
int c0=lst1>=1?sum[rk[lst1]]:pows[n];
int c1=fst0<=m?sum[rk[fst0]]:0;
printf("%d\n",(c0-c1+md)%md);
}
return 0;
}