【noi.ac #1759】ZYB的測驗計劃

題目

題目描述
ZYB是一名資深特級OI教師。在他的題庫裏,一共有M道判斷題。現在,他正在教授N名OI選手,作爲一名特級教師,他一眼就看出了每個學生對每道問題的答案是YES還是NO.

現在他希望出一場堂測,他會選擇這些判斷題當中的一個非空子集(2M−1種可能)。他希望這場堂測能有一些區分度。定義一道題目有區分度,當且僅當至少有一個學生回答YES,並且至少有一個學生回答NO,而一場堂測有區分度,當且僅當所有的題目都是有區分度的。

然而,這些學生有可能會咕咕咕。每個學生只有50%的可能出現在這堂課上進行堂測。現在ZYB想知道,對於每個不同的子集,有多大的概率這場堂測是有區分度的呢?

爲了方便起見,假設這2M−1個概率分別是Ans1,Ans2,…,Ans2M−1,你只需要輸出(Ans1×2Nmod998244353)xor(Ans2×2Nmod998244353)xor…xor(Ans2M−1×2Nmod998244353)的值.

輸入格式
第一行兩個整數n,m.

接下來n行,每行一個長度爲m的01字符串,其中第i位爲1表示該學生對這個問題回答YES,0表示回答NO.

輸出格式
一行輸出所求值。

樣例1
輸入

2 2
01
10
輸出

1
解釋 對於每道題目,當且僅當所有人都到時纔有區分度.故Ans1=Ans2=Ans3=1/4,帶入可知是1.

樣例2
輸入

4 2
00
01
10
11
輸出

7
解釋 對於第一題,當只有1,2或只有3,4到時是沒區分度的,所以可能性是(16-7)/16,第二題也是如此.

若需要兩題都有區分度,則需要1,4同時到或2,3同時到,則(1,4),(2,3)以及任意超過3個人到都是合法解。可能性爲7/16.

於是,答案爲9 xor 9 xor 7=7

數據範圍
對於30%的數據,M≤4.

對於60%的數據,M≤10.

對於100%的數據,N≤100000,M≤15

思路

我們考慮設fSf_S表示SS裏面的這些題目都有區分度的方案數。

那麼我們可以知道fS=SS(1)SGSf_S=\sum_{S' \sub S} (-1)^{|S'|}G_{S'},其中SS'SS的一個 子集,而gSg_S的含義是有多少種方案使得SS中的所有題都沒有區分度.

最後我們考慮如何計算gSg_S。我們設計一個hSh_S,SS是一個長度爲mm的三進制數,如果第ii位爲22表示這一位無所謂,然後如果 這一位爲0/10/1則表示這一位選擇0/10/1的數的個數. 設SS'SS中不爲22的位置集合,則gS+=2hS1g_{S'} += 2^{h_S} −1,最後再給每個gSg_S加上空集就行。

如何計算hSh_S?如果SS中沒有22,則可以直接算出來,否則我們隨便找一個22,將這個22分別變成0/10/1得到SS',SS′′,hS=hS+hSh_S = h_{S′} + h_{S′′},按照0+10 + 1的個數從小往大枚舉就 行,兩步的複雜度都是O(3M)O(3^M),總複雜度爲O(3M+NM)O(3^M + NM)

代碼

#include<bits/stdc++.h>
#define N 300005
#define Mo 998244353
using namespace std;
 
int f[15000005],n,pw3[N],pw2[N],g[1<<15],m,size[N],Cas;
 
char c[N];
 
void dfs(int u,int _2,int _3,int _4)
{
	if (u==m)
	{
		if (_4!=-1)
			f[_3]=f[_3-pw3[m-1-_4]]+f[_3-2*pw3[m-1-_4]];
		(g[_2]+=pw2[f[_3]]-1)%=Mo;
	}
	else
	{
		dfs(u+1,(_2<<1)+1,_3*3,_4);
		dfs(u+1,(_2<<1)+1,_3*3+1,_4);
		dfs(u+1,(_2<<1),_3*3+2,u);
	}
}
 
int main()
{
	pw2[0]=1; 
	for(int i=1; i<N;++i) pw2[i]=pw2[i-1]*2%Mo;
	for(int i=1; i<(1<<15);++i) size[i]=size[i/2]+(i&1); 
	pw3[0]=1; 
	for(int i=1; i<20;++i) pw3[i]=pw3[i-1]*3;
	scanf("%d%d",&n,&m);
	for(int i=1; i<=n;++i)
	{
		scanf("%s",c); int tmp=0;
		for(int j=0;j<m;++j)  tmp=tmp*3+(c[j]=='1');
		f[tmp]++;
	}
	dfs(0,0,0,-1); int yjy=0;
	for(int i=0; i<(1<<m);++i) (g[i]+=1)%=Mo;
	for(int i=1; i<(1<<m);++i)
	{
		int tmp=0;
		for(int j=i;;j=(j-1)&i)
		{
			if (size[j]&1) tmp=(tmp-g[j]+Mo)%Mo;
			else tmp=(tmp+g[j])%Mo;
			if (!j) break;
		}
		yjy^=tmp;
	}
	printf("%d\n",yjy);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章