51NOD 区间的价值 V2

时间:2023-03-09 09:53:50
51NOD 区间的价值 V2

http://www.51nod.com/contest/problem.html#!problemId=1674

因为题目要求的只是& 和 |

这两个运算。而这两个运算产生的值是有限的。

&,当产生的值是0的时候,就不会再变化了。

|,当产生的值是(111111111)的时候,值也不变化了。

所以每种运算的状态数也就30种不同的情况。

所以考虑分治,

先算出[mid + 1, R]中的值,就是固定mid + 1,一直向右边&和|

然后在[mid, L]从mid开始一直向左边&和|,这样的话,如果已经得到了右半区间的各种情况,就可以快速算出整个区间的情况。

把and起来得值和or起来得值进行统计,因为每种运算只有30种情况,所以当他们都一样的时候,就压缩在一起,统计有多少个不同的值即可。

30 + 30 = 60

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define IOS ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL; #include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
const int maxn = + ;
LL a[maxn];
LL ans;
const int MOD = ;
const LL one = ;
int AND[ + ], OR[ + ]; //全部是0或者全部是1后,状态不会变了
int sum[ + ]; //所以两种状态相同的时候,也就900种状态
void calc(int L, int R) {
if (L == R) {
ans = (ans + a[L] * a[L] % MOD) % MOD;
return;
}
int mid = (L + R) >> ;
int len = ;
AND[len] = OR[len] = a[mid + ];
sum[len] = ;
for (int i = mid + ; i <= R; ++i) {
if ((AND[len] & a[i]) == AND[len] && (OR[len] | a[i]) == OR[len]) {
sum[len]++;
} else {
++len;
AND[len] = AND[len - ] & a[i];
OR[len] = OR[len - ] | a[i];
sum[len] = ;
}
}
LL nowAnd = (one << ) - ;
LL nowOr = ;
for (int i = mid; i >= L; --i) {
nowAnd = nowAnd & a[i];
nowOr |= a[i];
for (int j = ; j <= len; ++j) { ans += (nowAnd & AND[j]) * (nowOr | OR[j]) % MOD * sum[j] % MOD;
ans %= MOD;
}
}
calc(L, mid);
calc(mid + , R);
}
void work() {
int n;
cin >> n;
for (int i = ; i <= n; ++i) {
cin >> a[i];
}
calc(, n);
cout << ans << endl;
}
int main() {
#ifdef local
freopen("data.txt","r",stdin);
#endif
IOS;
work();
return ;
}