Loj #2494. 「AHOI / HNOI2018」尋寶遊戲
Loj #2494. 「AHOI / HNOI2018」尋寶遊戲
題目描述
某大學每年都會有一次 Mystery Hunt 的活動,玩家需要根據設置的線索解謎,找到寶藏的位置,前一年獲勝的隊伍可以獲得這一年出題的機會。
作為新生的你對這個活動非常感興趣。你每天都要從西向東經過教學樓一條很長的走廊,這條走廊是如此的長,以至於它被人戲稱為 infinite corridor。一次,你經過這條走廊的時,註意到在走廊的墻壁上隱藏著 \(n\) 個等長的二進制的數字,長度均為 \(m\)。你從西向東將這些數字記錄了下來,形成一個含有 \(n\) 個數的二進制數組 \(a_1, a_2, ..., a_n\)
你需要插入恰好 \(n\) 個運算符,相鄰兩個數之間恰好一個,在第一個數的左邊還有一個。如果我們在第一個運算符的左邊補入一個 \(0\),這就形成了一個運算式,我們可以計算它的值。與往常一樣,運算順序是從左往右。有趣的是,出題人已經告訴你這個值的可能的集合——Voo Doo 雜誌裏的那一些二進制數 \(r_1, r_2, ..., r_q\)
輸入格式
第一行三個數 \(n, m, q\),含義如題所述。
接下來 \(n\) 行,其中第 \(i\) 行有一個長度為 \(m\) 的二進制串,左邊是最高位,表示 \(a_i\)。
接下來 \(q\)
輸出格式
輸出 \(q\) 行,每行一個數,其中第 \(i\) 行表示對應於 \(r_i\) 的答案。
數據範圍與提示
對於 \(10\%\) 的數據,\(n \le 20, m \le 30\),\(q = 1\)
對於另外 \(20\%\) 的數據,\(n \le 1000\),\(m \le 16\)
對於另外 \(40\%\) 的數據,\(n \le 500\),\(m \le 1000\)
對於 \(100\%\) 的數據,\(1 \le n \le 1000\),\(1 \le m \le 5000\),\(1 \le q \le 1000\)
\(\\\)
\(myy\)的題就是神仙啊!
首先我們對每一位單獨考慮,再將\(m\)位的一起考慮。
我們將操作序列也定義為一個\(01\)串。如果是\(and\)操作,則為\(1\),否則為\(0\)。
我們發現:
\[
x\ and\ 0=0\x\ or\ 1=1
\]
也就是說\(\ and\ 0\)和\(\ or\ 1\)本質上是賦值操作。
然後
\[
x\ and\ 1=x\x\ or\ 0=x
\]
也就是說\(\ and\ 1\)和\(\ or\ 0\)後上值不變。
然後考慮第\(j\)位,如果它應該為\(1\),則最後一個賦值操作後第\(j\)位變成了\(1\)。然後我們觀察操作序列和第\(j\)列的\(01\)串之間的關系。賦值操作的時候對應位置上的數不同。所以,我們以\(n\)為最高位,設第\(j\)列的數字為\(s_j\),操作序列的數字為\(R\),則\(R>s_j\)。
反之,則\(R\leq s_j\)。
所以我們將\(m\)列數字排序過後找到上下界就行了。
代碼:
#include<bits/stdc++.h>
#define ll long long
#define N 1005
#define M 5005
using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
const ll mod=1e9+7;
int n,m,q;
char a[N][M],r[M];
ll pw2[M];
int len;
const ll maxx=1<<30;
int rk[M];
struct node {
int id;
ll val[50];
bool operator <(const node &x)const {
for(int i=len;i>=1;i--) if(val[i]!=x.val[i]) return val[i]<x.val[i];
return id<x.id;
}
}s[M];
node operator -(node a,node b) {
for(int i=1;i<=len;i++) {
if(a.val[i]<b.val[i]) {
a.val[i+1]--;
a.val[i]+=maxx;
}
a.val[i]-=b.val[i];
}
return a;
}
ll bin[N];
int main() {
n=Get(),m=Get(),q=Get();
pw2[0]=1;
for(int i=1;i<=n;i++) pw2[i]=pw2[i-1]*2%mod;
for(int i=1;i<=n;i++) scanf("%s",a[i]+1);
len=(n-1)/30+1;
for(int i=1;i<=m;i++) s[i].id=i;
for(int j=n;j>=1;j--) {
int now=(j-1)/30+1;
for(int i=1;i<=m;i++) {
s[i].val[now]=(s[i].val[now]<<1)|(a[j][i]-'0');
}
}
sort(s+1,s+1+m);
for(int i=1;i<=m;i++) rk[s[i].id]=i;
for(int i=1;i<=n;i++) {
int now=(i-1)/30+1;
s[m+1].val[now]=(s[m+1].val[now]<<1)|1;
}
for(int i=1;i<len;i++) bin[i]=i*30;
bin[len]=bin[len-1]+(n-1)%30+1;
s[0].id=0,s[m+1].id=m+1;
int tim=0;
while(q--) {
scanf("%s",r+1);
int lx=0,rx=m+1;
for(int i=1;i<=m;i++) {
if(r[i]=='1') rx=min(rx,rk[i]);
else lx=max(lx,rk[i]);
}
if(lx>=rx) cout<<0<<"\n";
else {
ll ans=0;
node c=s[rx]-s[lx];
for(int i=1;i<=len;i++) (ans+=c.val[i]*pw2[bin[i-1]])%=mod;
if(rx==m+1) ans=(ans+1)%mod;
cout<<ans<<"\n";
}
}
return 0;
}
Loj #2494. 「AHOI / HNOI2018」尋寶遊戲