[BZOJ3811]瑪裏苟斯

題目

傳送門 to usOJ

題目概要
對於可重集合 SS ,設其元素個數爲 nn ,顯然它有 2n2^n 個子集。定義集合的權值爲其中元素的異或和,求子集的權值的 kk 次方的期望。即:sSf(s)k2n\frac{\sum_{s\subseteq S}f(s)^k}{2^n} 的值,f(s)f(s) 是集合內元素的異或和。

數據範圍與約定
n105,k5n\le 10^5,k\le 5

思路

FBL    WARNING使\bf{FBL\;\;WARNING}:我將試着使用奇怪的文風去論述這個問題。

剛看一眼,就被這道題給嚇住了,nn 很大,kk 卻小的可憐,55 站在 10510^5 旁邊,顯得毫不起眼。但我們不着眼於出題人的區別對待,我們要拯救每一個 kk ,讓世界重新充滿愛。

  • k=1k=1 的時候,這道題變得平淡無奇,味同嚼蠟。所有的 ff 中,有一半都滿足第 ii 位是一個 11 ,只要 SS 中存在一個數字可以提供這個 11 。這是因爲,這個數字是否出現,這件事不影響別人,它只負責自己的選擇,就像 zxyzxy 默默的 AKAK 。若夫它看到是 00 ,它便義無反顧地將自己融入整個羣體;至若此值已經爲 11 ,它不會出現在人們的視野中,好像從未出現過,又好像一直都在。
  • k=2k=2 的時候,我們要做的是一個平方的選擇。兩個元素,雖然不在一個括號中,但是二人的心卻在一起,這就是平方的真諦。二者何人?枚舉則知。不是所有人都能夠相遇,就像人生總會有很多遺憾。如果二者可以被獨立抉擇——存在一個數字,在第 ii 位爲 11 而第 jj 位爲 00 ——那麼二人是不容易遇到的,只有 12×12=14\frac{1}{2}\times\frac{1}{2}=\frac{1}{4} 。如果不是獨立抉擇,就會翻倍,得到 12\frac{1}{2} 了。
  • k3k\ge 3 的時候,我們終於可以大展拳腳了。我們有很多方法。我們可以利用答案小於 2632^{63} 的特點,直接猜到 a222a\le 2^{22} 次方,求出線性基,然後暴力枚舉。我們也可以繼續學習 k=2k=2 的方案。

我改悔了。

對於 k=2k=2 ,我們拆位,將一個數字拆成很多個 22 的冪之和。然後平方就是選兩個二進制位唄。


假設最大的一個二進制位是 2x2^x ,那麼至少有一半的異或和不小於 2x2^x ,與 k=1k=1 的情況類似。

所以答案至少有 12(2x)k<263\frac{1}{2}\cdot(2^x)^k<2^{63} ,可以解出 x<64kx<\frac{64}{k} ,然後就沒了。

作乘法可能會爆 unsigned  long  longunsigned\;long\;long ,稍微打的騷一點就好了。

代碼

// 湘妹兒,永遠滴神!
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
typedef unsigned long long int_;
int_ readint(){
	int_ x; scanf("%llu",&x);
	return x; // cin就能兼容了[滑稽]
}

int_ d[100];
void insert(int_ x){
	for(int i=63; i>=0&&x; --i)
		if(x>>i&1){
			if(d[i] == 0)
				d[i] = x;
			x ^= d[i];
		}
}

int n, k, cnt; // 分母是pow(2,cnt)
int_ zheng, xiao; // 整數與小數
void dfs(int t,int_ now){
	if(t == -1){
		int_ res = 1; // 加入res*now
		for(int i=1; i<k; ++i)
			res *= now;
		int_ yu = res%(1<<cnt);
		res = res/(1<<cnt);
		// 變成了res*(1<<cnt)+yu
		zheng += res*now;
		xiao += yu*now;
		zheng += xiao/(1<<cnt);
		xiao = xiao%(1<<cnt);
		return ;
	}
	if(d[t]) dfs(t-1,now);
	dfs(t-1,now^d[t]);
}
int main(){
	n = readint(), k = readint();
	for(int i=0; i<n; ++i)
		insert(readint());
	int_ all = 0;
	for(int i=63; i>=0; --i)
		all |= d[i];
	if(k == 1){
		cnt = 1;
		zheng = all>>1;
		xiao = all&1;
	}
	if(k == 2){
		cnt = 2;
		for(int i=32; i>=0; --i)
		if(all>>i&1)
		for(int j=32; j>=0; --j)
		if(all>>j&1){
			bool apart = false;
			for(int o=32; o>=0; --o)
				if((d[o]>>i&1)^(d[o]>>j&1))
					{ apart = true; break; }
			int mom = (apart ? 1 : 0)+1;
			if(i+j >= mom)
				zheng += (1ull<<(i+j-mom));
			else
				xiao += 1ull<<(i+j+cnt-mom);
			// 爲了讓分母pow(2,mom->cnt)
			zheng += xiao/(1<<cnt);
			xiao = xiao%(1<<cnt);
		}
	}
	if(k >= 3){
		for(int i=0; i<=63; ++i)
			if(d[i] > 0) ++ cnt;
		dfs(63,0);
	}
	printf("%llu",zheng);
	if(xiao) putchar('.');
	while(xiao *= 10){
		printf("%llu",xiao/(1<<cnt));
		xiao %= (1<<cnt);
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章