【HNOI2018尋寶游戲】


尋寶游戲

題意:

給定\(n\)個長度為\(m\)\(01\)

現在允許你在對\(0\)依次“按位與”或者“按位或”上這些\(01\)

\(q\)組詢問,每次詢問有多少種方法填入“按位與”或者“按位或”使得最終結果為詢問的\(01\)

\(Sol:\)

\(1.\)

\[0~|~1~=~1~,\quad 1~|~1~=~1 \]

\[0~\&~0=~0,\quad 1~\&~0~=~0 \]

於是有對於一個\(1\)如果其前面填入的為\(~|~\),那么無論如何答案都是\(1\)

對於一個\(0\)如果其前面填入的為\(\&\),那么無論如何答案都是\(0\)

\(2.\)

\[1~|~0~=~1~,\quad 0~|~0~=~0 \]

\[1~\&~1=~1,\quad 0~\&~1~=~0 \]

於是有對於一個\(1\)如果其前面為\(\&\)那么答案不會改變

對於一個\(0\)如果其前面為\(~|~\)那么答案也不會改變

\(3.\)

我們先考慮最基礎的問題,假設所有的\(0/1\)串都只有\(1\)

我們從后往前考慮所有的\(0/1\),可以將其變成一個序列

由於兩兩之間也有一個操作,於是把操作單獨提出來也是一個序列

規定它為操作序列,我們嘗試把操作序列轉成一個\(0/1\)序列

\(~|~\)\(0\)\(\&\)\(1\)

於是對於一個\(0/1\)序列\(10010\)等,其運算結果為\(1\)當且僅當其\(>\)操作序列,否則其為\(0\)

這是因為對於操作序列,如果偏高位與\(0/1\)序列全部相等,則為情況\(2\)運算結果不變,否則如果存在一個\(1\)且操作序列對應位為\(0\)則為情況\(1\),運算結果為\(1\)

於是對於一位的情況,我們成功將這道題轉化成了一道比大小的題目了\(qwq\)

對於每個\(0/1\)串若其有\(m\)位我們就對其每一位單獨考慮

若結果要求當且位為\(1\)則表明操作序列\(<\)當且位,否則表示操作序列\(\ge\)當前位

於是我們將所有的\(0/1\)串提前排個序,問題就只需要取\(0\)中最大值\(x\)\(1\)中最小值\(y\)\(y-x\)即方案數

一點細節

注意到我們最后要反向,還要對每一位單獨處理

於是好像有我們讀入的就是待排序的最低位,次低位...最高位

於是好像可以邊讀入邊基排...

復雜度\(O(nm+qm)\)

#include<bits/stdc++.h>
using namespace std;
#define rep( i, s, t ) for( register int i = s; i <= t; ++ i )
#define drep( i, s, t ) for( register int i = t; i >= s; -- i )
#define int long long
#define re register
int read() {
	char cc = getchar(); int cn = 0, flus = 1;
	while(cc < '0' || cc > '9') {  if( cc == '-' ) flus = -flus;  cc = getchar();  }
	while(cc >= '0' && cc <= '9')  cn = cn * 10 + cc - '0', cc = getchar();
	return cn * flus;
}
const int P = 1000000007 ; 
const int N = 1000 + 5 ;
const int M = 5000 + 5 ; 
int n, m, q, lk[M], rk[M], b[M][N], a[M], Ans[M] ; 
char s[M] ;
int Get( int x, int y ) {
	return ( Ans[y] - Ans[x] + P ) % P ; 
}
signed main()
{
	n = read(), m = read(), q = read() ; 
	rep( i, 1, m ) rk[i] = i ; 
	rep( i, 1, n ) {
		scanf("%s", s + 1 ) ; int rs = 0 ; 
		rep( j, 1, m ) a[j] = s[j] - '0', b[j][i] = a[j] ; 
		rep( j, 1, m ) if( a[rk[j]] == 0 ) lk[++ rs] = rk[j] ;
		rep( j, 1, m ) if( a[rk[j]] == 1 ) lk[++ rs] = rk[j] ;
		rep( j, 1, m ) rk[j] = lk[j] ; 
	}
	rep( j, 1, m ) drep( i, 1, n ) Ans[j] = ( Ans[j] * 2ll + b[j][i] ) % P ;
	rep( j, 1, n ) Ans[m + 1] = ( Ans[m + 1] * 2ll + 1 ) % P ; 
	rk[m + 1] = m + 1 ; Ans[m + 1] += 1 ; 
	while( q -- ) {
		scanf("%s", s + 1 ) ; int Lk = 0, Rk = m + 1 ; 
		rep( j, 1, m ) if( s[rk[j]] == '1' ) { Rk = j ; break ; }
		drep( j, 1, m ) if( s[rk[j]] == '0' ) { Lk = j ; break ; } 
		printf("%lld\n", ( Rk < Lk ) ? 0 : Get( rk[Lk], rk[Rk] ) ) ;
	} 
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM