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
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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 = 100000 + 20; LL a[maxn]; LL ans; const int MOD = 1000000007; const LL one = 1; int AND[900 + 2], OR[900 + 2]; //全部是0或者全部是1后,状态不会变了 int sum[900 + 2]; //所以两种状态相同的时候,也就900种状态 void calc(int L, int R) { if (L == R) { ans = (ans + a[L] * a[L] % MOD) % MOD; return; } int mid = (L + R) >> 1; int len = 1; AND[len] = OR[len] = a[mid + 1]; sum[len] = 1; for (int i = mid + 2; i <= R; ++i) { if ((AND[len] & a[i]) == AND[len] && (OR[len] | a[i]) == OR[len]) { sum[len]++; } else { ++len; AND[len] = AND[len - 1] & a[i]; OR[len] = OR[len - 1] | a[i]; sum[len] = 1; } } LL nowAnd = (one << 31) - 1; LL nowOr = 0; for (int i = mid; i >= L; --i) { nowAnd = nowAnd & a[i]; nowOr |= a[i]; for (int j = 1; j <= len; ++j) { ans += (nowAnd & AND[j]) * (nowOr | OR[j]) % MOD * sum[j] % MOD; ans %= MOD; } } calc(L, mid); calc(mid + 1, R); } void work() { int n; cin >> n; for (int i = 1; i <= n; ++i) { cin >> a[i]; } calc(1, n); cout << ans << endl; } int main() { #ifdef local freopen("data.txt","r",stdin); #endif IOS; work(); return 0; }