清华集训2014 玛里苟斯

  • 清华集训2014 玛里苟斯
  • 求子集异或和k次方的期望。
  • 异或考虑按位算贡献。
  • 对于\(K=1\),考虑异或和\(\frac{x}{2}\)就是答案。
  • 证明简单来说就是,你可以先打一个概率\(dp\)分别对每一位来考虑。
  • 假设当前考虑的位数是\(j\),如果\(i\)这个数是\(0\),那么选和不选的影响都是不改变原来的选取,如果\(i\)这个数是\(1\),那么选与不选是对称的,也就是把原来\(0\)\(1\)的概率相互交换。
  • 实际上我们在模拟一个或运算。
  • 对于\(K=2\),首先每一位如果有数,都会贡献\(4^j\)答案。
  • 如果异或和的第i位和第j为都有数,会贡献\(2^{i+j-1}\)的答案,因为同时出现的概率是\(\frac {1}{4}\)
  • 但是如果两位不是互相独立的,贡献的答案应该是\(2^{i+j}\),因为同时出现的概率是\(\frac {1}{2}\)
  • \(k>=3\),首先,线性基的定义就证明了,一个数集的异或子集,和他随便乱异或的数集的异或子集是等效的。
  • 所以可以线性基直接消掉线性相关的数。
  • 由于答案在\(2^{63}\)以内,所以线性基的大小不会超过\(22\),直接暴力枚举计算期望。
  • 这题有一个结论是答案两倍一定是整数,也就是答案的小数最多有一位;
  • \(k\leq3\)比较好证,但是\(k>3\)怎么证明啊……??
  • upd,找到证明
#include<bits/stdc++.h>
#define R register int
#define db long double
#define ll unsigned long long 
using namespace std;
const int N=200001;
int n,K;ll w[N];
ll gi(){
    ll x=0;R k=1;char c=getchar();
    while(c!='-'&&(c<'0'||c>'9'))c=getchar();
    if(c=='-')k=-1,c=getchar();
    while(c<='9'&&c>='0')x=(x<<3)+(x<<1)+c-'0',c=getchar();
    return x*k;
}
namespace cpp1{
    ll ans;
    void Main(){
        for(R i=1;i<=n;++i)ans|=w[i];
        printf("%lld",ans>>1ll);
        if(ans&1)cout<<".5";
    }
}
namespace cpp2{
    int hv[33];ll ans,res;
    int check(R p,R q){
        for(R i=1;i<=n;++i){
            if((w[i]&(1<<p))&&!(w[i]&(1<<q)))return 1;
            if((w[i]&(1<<q))&&!(w[i]&(1<<p)))return 1;
        }
        return 0;
    }
    void Main(){
        for(R i=1;i<=n;++i)res|=w[i];
        for(R i=0;i<33;++i){
            if((res&(1ll<<i))==0)continue;
            ans+=(1ll<<(i+i));
            for(R j=i+1;j<33;++j)
                if(res&(1ll<<j)){
                    if(check(i,j))ans+=(1ll<<(i+j));
                    else ans+=(2ll<<(i+j));
                }
        }
        printf("%lld",ans>>1);puts(ans&1?".5":"");
    }
}
namespace cpp3{
    int Mx,tot,len;ll res,num[300],now[300];__int128 ans;
    void ins(ll x){
        for(R j=0;j<Mx;++j){
            if((x&(1<<j))==0)continue;
            if(!num[j]){num[j]=x;return ;}
            x^=num[j];
        }
    }
    void Dfs(R i,ll nw){
        if(i==len+1){
            __int128 fin=1;
            for(R j=1;j<=K;++j)fin*=nw;
            ans+=fin,tot++;return ;
        }
        Dfs(i+1,nw),Dfs(i+1,nw^now[i]);
    }
    void Main(){
        for(R i=1;i<=n;++i){
            ll x=w[i];R nw=0;
            while(x)nw++,x>>=1;
            Mx=max(Mx,nw),res|=w[i];
        }
        for(R i=1;i<=n;++i)ins(w[i]);
        for(R i=0;i<Mx;++i)if(num[i])now[++len]=num[i];
        Dfs(1,0),ans>>=(len-1);ll res=ans>>1;
        printf("%lld",res),puts((ans&1)?".5":"");
    }
}
int main(){
    n=gi(),K=gi();
    for(R i=1;i<=n;++i)w[i]=gi();
    if(K==1)cpp1::Main();
    if(K==2)cpp2::Main();
    if(K>=3)cpp3::Main();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Tyher/p/10062176.html