http://acm.uestc.edu.cn/#/contest/show/54
H - Hug the princess
Time Limit: 3000/1000MS (Java/Others) Memory Limit: 65535/65535KB (Java/Others)
There is a sequence with n elements. Assuming they are a1,a2,⋯,an.
Please calculate the following expession.
In the expression above, ^ | & is bit operation.
Input
The first line contains a single integer n, which is the size of the sequence.
The second line contains n integers, the ith integer ai is the ith element of the sequence.
1≤n≤100000,0≤ai≤100000000
Output
Print the answer in one line.
Sample input and output
Sample Input | Sample Output |
---|---|
2 1 2 |
6 |
思路:首先,自己可以通过举几个例子来验证,异或运算与与运算之和刚好等价于或运算,或者可以这样想,异或是(1,0)、(0,1),与是(1,1),合起来刚好是或。然后题目就是求两倍的或运算了。然后,每一个ai都与aj或运算(i<j),每次ai与aj或的时候,aj二进制位上是1的数位在或运算后总还是1,所以前面有多个ai与aj或,最后结果里就有多少个aj的和;然后考虑aj上是0的数位(比如第4位),记录前面所有的ai中哪些数在这个第4位上是1,记为sum[4],最后的结果里就有sum[4]*(1<<4),因为有权值的嘛。这样O(n)地扫一遍,就行了。如果就像我第一次那样直接O(n*n)地把所有数进行或运算,呵呵,tle了。
这是官方题解:
代码:
1 #include <fstream> 2 #include <iostream> 3 #include <algorithm> 4 #include <cstdio> 5 #include <cstring> 6 #include <cmath> 7 #include <cstdlib> 8 #include <vector> 9 10 using namespace std; 11 12 #define PI acos(-1.0) 13 #define EPS 1e-10 14 #define lll __int64 15 #define ll long long 16 #define INF 0x7fffffff 17 18 ll a[100005]; 19 vector<ll> cnt; 20 21 int main(){ 22 //freopen("D:\input.in","r",stdin); 23 //freopen("D:\output.out","w",stdout); 24 int n; 25 ll tn=0; 26 scanf("%d",&n); 27 for(int i=0;i<n;i++){ 28 scanf("%lld",&a[i]); 29 tn=max(tn,a[i]); 30 } 31 ll ans=0,t=0; 32 while(tn){ 33 t++; 34 tn>>=1; 35 cnt.push_back(0); 36 } 37 for(int i=0;i<n;i++){ 38 ans+=a[i]*i; 39 for(int j=0;j<t;j++){ 40 if(!((a[i]>>j)&1)){ 41 ans+=(1<<j)*cnt[j]; 42 }else{ 43 cnt[j]++; 44 } 45 } 46 } 47 printf("%lld ",ans*2); 48 return 0; 49 }