UESTC 84 Binary Operations(算贡献求期望)

题意:给你一个长度n的数列,求出所有区间的and,or,xor的值的期望


题解:这种题直接求比较麻烦,一般位运算的题就是字典树和按位枚举呗,这题就肯定是按位枚举,于是可以求出第i个数字的第j位对以它为区间右端点的贡献。
最后除以区间个数 n(n+1)2 即可
下面来看and的贡献,_and[i][j]能够贡献的区间个数是需要前面连续多个1,所以找从i开始往前碰到的第一个0的位置
_or[i][j]能够贡献的区间个数是区间内只要有1即可,所以往前找到第一个1出现的位置
_xor[i][j]能够贡献的区间个数是当前到第i位,1的个数是奇数还是偶数,然后找前面1的个数是相反的区间个数(奇数就找偶数,偶数就找奇数)


代码:

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")

using namespace std;
#define   MAX           50005
#define   MAXN          6005
#define   maxnode       15
#define   sigma_size    30
#define   lson          l,m,rt<<1
#define   rson          m+1,r,rt<<1|1
#define   lrt           rt<<1
#define   rrt           rt<<1|1
#define   middle        int m=(r+l)>>1
#define   LL            long long
#define   ull           unsigned long long
#define   mem(x,v)      memset(x,v,sizeof(x))
#define   lowbit(x)     (x&-x)
#define   pii           pair<int,int>
#define   bits(a)       __builtin_popcount(a)
#define   mk            make_pair
#define   limit         10000

//const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const LL     INFF  = 0x3f3f;
const double pi    = acos(-1.0);
//const double inf   = 1e18;
const double eps   = 1e-8;
const LL    mod    = 1e9+7;
const ull    mx    = 133333331;

/*****************************************************/
inline void RI(int &x) {
      char c;
      while((c=getchar())<'0' || c>'9');
      x=c-'0';
      while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
 }
/*****************************************************/

int a[MAX];
LL _and[MAX];
LL _or[MAX];
LL _xor[MAX];
int pre1[MAX];
int pre0[MAX];
int main(){
    int t,kase=0;
    cin>>t;
    while(t--){
        kase++;
        printf("Case #%d: ",kase);
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++) RI(a[i]);
        LL aa=0,bb=0,cc=0;
        for(int i=30;i>=0;i--){
            _and[0]=0;
            _or[0]=0;
            _xor[0]=0;
            pre1[0]=0,pre0[0]=0;
            int odd=0,even=1;
            int num1=0;
            for(int j=1;j<=n;j++){
                if((a[j]>>i)&1){
                    pre1[j]=max(j,pre1[j-1]);
                    pre0[j]=pre0[j-1];
                    num1++;
                }
                else pre1[j]=pre1[j-1],pre0[j]=max(j,pre0[j-1]);
                if(num1%2) odd++;
                else even++;
                _and[j]=_and[j-1]+(LL)(j-pre0[j])*(1LL<<i);
                _or[j]=_or[j-1]+(LL)pre1[j]*(1LL<<i);
                if(num1%2) _xor[j]=_xor[j-1]+(LL)even*(1LL<<i);
                else _xor[j]=_xor[j-1]+(LL)odd*(1LL<<i);
            }
            aa+=_and[n];
            bb+=_or[n];
            cc+=_xor[n];
        }
        printf("%.6f %.6f %.6f\n",aa*1.0/n/(n+1)*2,bb*1.0/n/(n+1)*2,cc*1.0/n/(n+1)*2);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值