1. 程式人生 > 其它 >[JOI 2018 Final]毒蛇越獄

[JOI 2018 Final]毒蛇越獄

\[題意:給你一個S,每個位置上為0/1/?\\ 讓你求ans = \sum_{S可能出現}f_S\\ 詢問次數很多,每次要做到O(2^6) \]

sol.

考慮\(S <= 20\),考慮鴿巢原理

\[min\{cnt_0,cnt_1,cnt_{?}\} <= 6 \]

考慮一下幾種情況

\(1.cnt_0\)最小

\[ans = \sum_{S\in S_0}(-1)^{|S|}g_{S ^ S_1} \]

\(1.cnt_1\)最小

\[ans = \sum_{S\in S_1}(-1)^{|S|}f_{S \oplus S_1 | f_{?}} \]

\(1.cnt_?\)最小

\[ans = \sum_{S\in S_{?}}a_{S \oplus S_1} \]

其中\(f_S,g_S\)分別是原來的高維字首字尾和

#include<bits/stdc++.h>
#define MAXN 2000005
typedef long long ll;
using namespace std;



int L,Q;

void fwt1(ll f[] , int tp){
	for(int j = 0 ; j < L ; j++){
		for(int S = 0 ; S < (1 << L) ; S++){
			if(S & (1 << j)){
				f[S] = f[S] + f[S ^ (1 << j)];	
			}
		}
	}
}
void fwt2(ll f[] , int tp){
	for(int j = 0 ; j < L ; j++){
		for(int S = (1 << L) - 1 ; S >= 0 ; S--){
			if((S & (1 << j)) == 0)f[S] = f[S] + f[S ^ (1 << j)];
		}
	}
}

ll f[MAXN],g[MAXN],cnt[MAXN],pl[MAXN],a[MAXN];
int maxS;
int S1,S2,S3;//0 , 1 , ?
int cnt1,cnt2,cnt3;
char s[MAXN];


int main(){
	//freopen("01-03.in" , "r" , stdin);
	scanf("%d%d" , &L , &Q);
	scanf("%s" , s + 1);
	for(int i = 0 ; i < (1 << L) ; i++){
		f[i] = (s[i + 1] - '0');
		g[i] = (s[i + 1] - '0');
		a[i] = (s[i + 1] - '0');
	}
	fwt1(f , 1);
	fwt2(g , 1);
	for(int i = 0 ; i < (1 << L) ; i++)cnt[i] = cnt[i >> 1] + (i & 1);
	pl[0] = 1;for(int i = 1 ; i < (1 << L) ; i++){
		if(i & 1)pl[i] = pl[i >> 1] * (-1);
		else pl[i] = pl[i >> 1];
	}
	maxS = (1 << L) - 1;
	while(Q--){
		ll ans = 0;
		S1 = S2 = S3 = 0;
		scanf("%s" , s + 1);
		for(int i = 1 ; i <= L ; i++){
			if(s[L - i + 1] == '0')S1 |= (1 << (i - 1));
			if(s[L - i + 1] == '1')S2 |= (1 << (i - 1));
			if(s[L - i + 1] == '?')S3 |= (1 << (i - 1));
		}
		cnt1 = cnt[S1] , cnt2 = cnt[S2] , cnt3 = cnt[S3];
		if(cnt3 <= min(cnt1 , cnt2)){
			for(int S = S3 ; ; S = S3 & (S - 1)){
				ans = ans + a[S | S2];
				if(S == 0)break;
			}
		}
		else if(cnt1 <= min(cnt2 , cnt3)){
			for(int S = S1 ; ; S = S1 & (S - 1)){
				ans = ans + pl[S] * g[S2 | S];
				if(S == 0)break;
			}
		}
		else if(cnt2 <= min(cnt1 , cnt3)){
			for(int S = S2 ;  ; S = S2 & (S - 1)){
				ans = ans + pl[S] * f[(S2 ^ S) | S3];
				if(S == 0)break;
			}
		}
		cout<<ans<<endl;
	}
	
	
	
}