题意:
N<=105数字序列,求该序列的所有子串的与、或、异或操作之后的期望
分析:
听起来期望很可怕,其实就是sum:=所有子串的某个操作的和,tot:=所有子串的个数(n+1)n/2,答案就是sum/tot
问题是怎么求呢,暴力枚举肯定不行,O(n2)就T了,考虑这样的dp状态.
dp[i]:=某种操作的以a[i]结尾的所有子串的贡献,暴力枚举不行那么我们按位枚举
and[i]:=and操作以a[i]结尾的所有子串的1的个数,or[i],xor[i]同理
对于a[i]这个数:
如果当前位是1
考虑and的性质,那么and[i]=and[i]+1
or 1的话就全部都是1咯,or[i]=i
xor 1相反,之前的0变1,xor[i]=i−xor[i−1](0数=总数−1数)
如果当前位是0
and 0的话不都是0,and[i]=0
or 0不影响结果就不变,or[i]=or[i−1]
xor 0,任何一个数xor 0都等于它本身,所以也不变,xor[i]=xor[i−1]
问题得以在O(nlogn)时间解决
代码:
//
// Created by TaoSama on 2015-10-07
// Copyright (c) 2015 TaoSama. All rights reserved.
//
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
using namespace std;
#define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl
const int N = 5e4 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
typedef long long LL;
int n, a[N], _and[N], _or[N], _xor[N];
int main() {
#ifdef LOCAL
freopen("in.txt", "r", stdin);
// freopen("out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
int t; scanf("%d", &t);
int kase = 0;
while(t--) {
scanf("%d", &n);
for(int i = 1; i <= n; ++i) scanf("%d", a + i);
double ans1 = 0, ans2 = 0, ans3 = 0;
for(int i = 0; i < 31; ++i) {
int mul = 1 << i;
for(int j = 1; j <= n; ++j) {
if(a[j] & mul) {
_and[j] = _and[j - 1] + 1;
_or[j] = j;
_xor[j] = j - _xor[j - 1];
} else {
_and[j] = 0;
_or[j] = _or[j - 1];
_xor[j] = _xor[j - 1];
}
ans1 += 1.0 * _and[j] * mul;
ans2 += 1.0 * _or[j] * mul;
ans3 += 1.0 * _xor[j] * mul;
}
}
double tot = 1.0 * (n + 1) * n / 2;
printf("Case #%d: %.6f %.6f %.6f\n", ++kase,
ans1 / tot, ans2 / tot, ans3 / tot);
}
return 0;
}