1. 程式人生 > >bzoj 3811: 瑪裡苟斯 高斯消元&dfs

bzoj 3811: 瑪裡苟斯 高斯消元&dfs

       這道題目還要根據k來分類討論。。。。

       當k=1的時候,按位求貢獻,然後發現答案就是所有數or起來再/2。

       當k=2的時候,就真的要按位來了。。按照(x1+x2+...+xn)^2展開x1x1+x1x2+x1x3...+xnxn,列舉i,j表示後面式子的下標。然後一個數就變成(0/1,0/1)二元組,當選出的數二元組異或後為(1,1)的時候有2^(i+j)的貢獻。顯然根據k=1發現(1,1)概率應該是1/4,但特殊情況就是所有二元組都是(0,0)或者(1,1)的形式則概率為1/2。

       當k>2的時候,顯然每個數<=2^21,而如果一個數能通過別的數異或得到則沒有用。維護一個線性基然後就只有22個數了,dfs即可。

AC程式碼如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define M 75
#define ll unsigned long long
using namespace std;

int n,flag; ll ans,res,mod,bin[M],a[N],base[M]; bool f[M][M];
void calc(){
	int i,j;
	for (i=1; i<=n; i++)
		for (j=31; j>=0; j--) if (a[i]&bin[j])
			if (!base[j]){
				base[j]=a[i]; break;
			} else a[i]^=base[j];
	for (j=n=0; j<32; j++) if (base[j]) a[++n]=base[j];
}
void solve1(){
	int i,j,k,t;
	for (i=0; i<32; i++)
		for (j=1; j<=n; j++) f[i][j]=(a[j]&bin[i])?1:0;
	for (i=0; i<32; i++)
		for (j=0; j<32; j++){
			for (k=1; k<=n; k++) if (f[i][k]) break;
			if (k>n) continue;
			for (k=1; k<=n; k++) if (f[j][k]) break;
			if (k>n) continue;
			t=0;
			for (k=1; k<=n && !t; k++)
				if (f[i][k]!=f[j][k]) t=1;
			if (i+j-1-t<0) res++; else ans+=bin[i+j-1-t];
			ans+=res>>1; res&=1;
		}
	printf("%llu",ans); puts(res?".5":"");
}
void dfs(int k,ll now){
	if (k>n){
		int i; ll u=0,v=1;
		for (i=1; i<=flag; i++){
			u*=now; v*=now;
			u+=v>>n; v&=mod;
		}
		ans+=u; res+=v;
		ans+=res>>n; res&=mod;
		return;
	}
	dfs(k+1,now); dfs(k+1,now^a[k]);
}
void solve2(){
	mod=bin[n]-1; dfs(1,0);
	printf("%llu",ans); puts(res?".5":"");
}
int main(){
	scanf("%d%d",&n,&flag); int i;
	bin[0]=1; for (i=1; i<63; i++) bin[i]=bin[i-1]<<1;
	for (i=1; i<=n; i++) scanf("%llu",&a[i]);
	if (flag==1){
		for (i=1; i<=n; i++) ans|=a[i];
		printf("%llu",ans>>1); puts((ans&1)?".5":"");
		return 0;
	}
	calc();
	if (flag==2) solve1(); else solve2();
	return 0;
}


by lych

2016.5.9