zoukankan      html  css  js  c++  java
  • UOJ #36 -【清华集训2014】玛里苟斯(线性基+暴搜)

    UOJ 题面传送门

    看到 (k) 次方的期望可以很自然地想到利用低次方和维护高次方和的套路进行处理,不过。由于这里的 (k) 达到 (5),直接这么处理一来繁琐,二来会爆 long long,因此考虑另辟蹊径。注意到答案 (le 2^{63}-1),也就是说当 (k) 比较大时值域也不会太大。因此考虑对 (k) 分类讨论。

    (k=1) 时考虑计算每一位的贡献,注意到对于一位 (i),如果存在某个 (a_j) 满足 (a_j)(2^i) 位为 (1),那么这一位在子集异或和中为 (1) 的概率就是 (dfrac{1}{2})。否则这一位自然不可能异或出 (0) 来,对答案的贡献自然就是 (0),贡献随便加一下即可。

    (k=2) 时考虑用低次方和维护高次方和的套路,假设 (x=2^{b_1}+2^{b_2}+cdots+2^{b_k}),那么显然有 (x^2=(2^{b_1}+2^{b_2}+cdots+2^{b_k})^2=sumlimits_{i=1}^ksumlimits_{j=1}^k2^{b_i}·2^{b_j}),也就是说我们考虑每两个在异或和中同时为 (1) 的位 (i,j),答案加上 (2^i,2^j)(x) 中同时为 (1) 的概率乘上 (2^{i+j}) 的和。考虑枚举 (i,j) 计算 (2^i,2^j) 位在 (x) 中同时为 (1) 的概率,显然满足不存在任何一个 (a_p) 的第 (i) 位为 (1),或者不存在任何一个 (a_p) 的第 (i) 位为 (1),那么 (2^i,2^j) 位在 (x) 中同时出现的概率为 (0)。否则如果对于任意 (a_p) 都有 (a_p) 的第 (i,j) 位同时为 (0) 或者同时为 (1),那么 (2^i,2^j)(x) 中同时出现的概率为 (dfrac{1}{2}),否则 (2^i,2^j)(x) 中同时出现的概率为 (dfrac{1}{4}),贡献累加一下即可。

    (kge 3) 时我们考虑建出原序列的线性基,显然对于不在线性基中的元素,我们删除它们后答案不会发生变化。由于题目保证了答案 (le 2^{63}-1),因此线性基中的个数不会太多。精确地估算一下大概就 (sumlimits_{i=0}^{2^p-1}x^3) 是关于 (2^p) 的四次多项式,再除以一个 (2^p) 是关于 (2^p) 的三次多项式,当 (p=22)(4194304^3) 就已经超过了 ull 的范围,就算乘上个小常数也超过了 (2^{63}),因此暴搜即可。

    可以证明此题答案小数点后最多只有一位,证明大概就仿照 (k=2) 的思路,考虑枚举 (k)(b_1,b_2,b_3,cdots,b_k),答案加上 (b_1,b_2,cdots,b_k) 位同时为 (1) 的概率乘上 (2^{b_1+b_2+cdots+b_k})。我们假设 (b_1,b_2,b_3,cdots,b_k) 中不同数的个数为 (c),那么这些位产生的贡献应是某个整数乘上 (2^{b_1+b_2+cdots+b_k-c}),分析一下可知当 (b_i=0,c=1)(2^{b_1+b_2+cdots+b_k-c}) 取到最小值 (0.5),因此此题小数点后最多只有一位 0.5

    const int MAXN=1e5;
    const int LOG_N=64;
    int n,k,flg[LOG_N+2];u64 a[MAXN+5];
    void print(u64 x){
    	printf("%llu",x>>1);
    	if(x&1) printf(".5");
    }
    namespace sub{
    	u64 b[LOG_N+2];
    	__int128_t res=0;int tot=0;
    	void insert(u64 x){
    		for(int i=LOG_N-1;~i;i--) if(x>>i&1){
    			if(!b[i]) return b[i]=x,void();
    			x^=b[i];
    		}
    	}
    	void dfs(int x,u64 sm){
    		if(x==LOG_N+1){
    			__int128_t mul=1;
    			for(int i=1;i<=k;i++) mul*=sm;
    			res+=mul;tot++;return;
    		} dfs(x+1,sm);
    		if(b[x]) dfs(x+1,sm^b[x]);
    	}
    	void solve(){
    		for(int i=1;i<=n;i++) insert(a[i]);
    		dfs(0,0);
    		if(tot!=1) res/=(tot/2);
    		else res*=2;
    		print((u64)res);
    	}
    }
    int main(){
    	scanf("%d%d",&n,&k);
    	for(int i=1;i<=n;i++) scanf("%llu",&a[i]);
    	if(k==1){
    		u64 res=0;
    		for(int i=0;i<LOG_N;i++){
    			bool has1=0;
    			for(int j=1;j<=n;j++) has1|=(a[j]>>i&1);
    			if(has1) res+=1ull<<i;
    		} print(res);
    	} else if(k==2){
    		u64 res=0;
    		for(int i=0;i<LOG_N;i++) for(int j=1;j<=n;j++) flg[i]|=(a[j]>>i&1);
    		for(int i=0;i<LOG_N;i++) for(int j=0;j<LOG_N;j++){
    			bool sm=1;
    			if(!flg[i]||!flg[j]) continue;
    			for(int k=1;k<=n;k++) sm&=(!((a[k]>>i&1)^(a[k]>>j&1)));
    			if(sm) res+=1ull<<(i+j);
    			else res+=1ull<<(i+j-1);
    		} print(res);
    	} else sub::solve();
    	return 0;
    }
    
  • 相关阅读:
    Calendar日历类
    DateFormat类和SimpleDateFormat类
    Date时间类(java.util.Date)
    时间处理相关类
    不可变和可变字符序列使用陷阱
    String类
    搬圆桌问题
    重温经典之排序 java实现
    i++ 和 ++i
    Intellij Idea 使用技巧 updating
  • 原文地址:https://www.cnblogs.com/ET2006/p/UOJ-36.html
Copyright © 2011-2022 走看看