第一行一個數(shù)n(1<=n<=100000)。接下來一行n個數(shù)ai,表示這n個數(shù)(0<=ai<=10^9)。Output一行表示答案。Input示例33 4 5Output示例70思路:
完全沒思路,看網(wǎng)上題解的。分治,這方面能力還是弱。對于一段區(qū)間[l,r],求出中點mid,然后遞歸解決[l,mid-1]和[mid,r]的子問題,還有就是題目所要求的區(qū)間左端點在[l,mid-1],右端點在[mid,r]的情況,這里其實就是在[l,mid-1]中枚舉左端點,然后在[mid,r]中枚舉右端點。這里關(guān)鍵要注意到,在求[mid,r]內(nèi)的and前綴和以及or前綴和會發(fā)現(xiàn)大部分的值都是相同的,因為1e9的二進制位數(shù)只有大約log(1e9)位,這樣只要把[mid,r]區(qū)間的前綴和的值壓縮一下,數(shù)值相同的一段看作一個數(shù),并把這段長度儲存在cnt數(shù)組中,這樣前綴和長度不會超過log(1e9)。這樣再對[l,mid-1]區(qū)間遍歷一遍,對每個位置再暴力掃一遍[mid,r]的前綴和,復(fù)雜度是O(n*logn)。這樣分治,最后總復(fù)雜度是O(nlognlogn)。代碼:
#include <bits/stdc++.h>using namespace std;typedef long long ll;const int MAXN = 1e5 + 10;const ll MOD = 1e9 + 7;ll ans;ll a[MAXN], cnt[MAXN], OR[MAXN], AND[MAXN];void solve(int l, int r) { if (l == r) return; int mid = (l + r + 1) >> 1; int pos = mid; OR[pos] = AND[pos] = a[mid]; cnt[pos] = 1; for (int i = mid + 1; i <= r; i++) { if (OR[pos] != (OR[pos] | a[i]) || AND[pos] != (AND[pos] & a[i])) { // 當前and(or)前綴和與前一個and(or)前綴和不一致。 ++pos; OR[pos] = OR[pos - 1] | a[i]; AND[pos] = AND[pos - 1] & a[i]; cnt[pos] = 1; } else ++cnt[pos]; } ll resor = a[mid - 1], resand = a[mid - 1]; for (int i = mid - 1; i >= l; i--) { resor |= a[i]; resand &= a[i]; for (int j = mid; j <= pos; j++) { ans = (ans + (resor | OR[j]) * (resand & AND[j]) % MOD * cnt[j] % MOD) % MOD; } } solve(l, mid - 1); solve(mid, r);}int main() { int n; scanf("%d", &n); ans = 0; for (int i = 1; i <= n; i++) { scanf("%I64d", &a[i]); ans = (ans + a[i] * a[i] % MOD) % MOD; } solve(1, n); PRintf("%I64d/n", ans); return 0;}
新聞熱點
疑難解答