题意
给n个数,可以从这n个数中连续取任意个数进行与、或、异或运算。求与、或、异或运算的期望。
题解
设dp[i]为最后一个数字为i,满足条件的数列个数。运算按位进行,求出运算后使该位等于1的数列个数,然后乘以权值,再除以(n+1)*n/2,即可得到期望。对于每种运算,状态转移分两种情况。
该位为1:
与运算:dpa[i]=dpa[i-1]+a (a代表该数字前连续为1的数字个数)
或运算:dpo[i]=dpo[i-1]+i(i代表该数字前数字个数)
异或运算:dpx[i]=dpx[i-1]+i-v(v代表该数字前数字进行异或运算,结果为1的情况个数)
该位为0:
与运算:dpa[i]=dpa[i-1]
或运算:dpo[i]=dpo[i-1]+b(i代表该数字前数字进行或运算,结果为1的情况个数)
异或运算:dpx[i]=dpx[i-1]+v(v代表该数字前数字进行异或运算,结果为1的情况个数)
注意事项
dp[i]可能会很大,因此要用long long
代码
#include <iostream>
#include<cstdio>
#include<algorithm>
#define MAXN 50010
typedef long long ll;
int num[MAXN];
ll dpa[MAXN],dpo[MAXN],dpx[MAXN];
int n;
using namespace std;
void dp(){
dpa[0]=0,dpo[0]=0,dpx[0]=0;
int a=0,b=0,v=0;
for(int i=1;i<=n;i++){
if(1&num[i]){
a++;
dpa[i]=dpa[i-1]+a;
dpo[i]=dpo[i-1]+i;
dpx[i]=dpx[i-1]+i-v;
b=i;
v=i-v;
}else{
a=0;
dpa[i]=dpa[i-1];
dpo[i]=dpo[i-1]+b;
dpx[i]=dpx[i-1]+v;
}
num[i]=num[i]>>1;
}
}
int main()
{
int t;
scanf("%d",&t);
for(int ks=1;ks<=t;ks++){
scanf("%d",&n);
int maxnum=0;
for(int i=1;i<=n;i++){
scanf("%d",&num[i]);
maxnum=max(maxnum,num[i]);
}
int j=1;
double ans1=0,ans2=0,ans3=0;
while(maxnum>0){
dp();
ans1+=dpa[n]*j;
ans2+=dpo[n]*j;
ans3+=dpx[n]*j;
j=j<<1;
maxnum=maxnum>>1;
}
double x=(double)n*(n+1)/2;
printf("Case #%d: ",ks);
printf("%.6f %.6f %.6f\n",ans1/x,ans2/x,ans3/x);
}
return 0;
}