Codeforces 878 E. Numbers on the blackboard
解题思路
有一种最优策略是每次选择最后面一个大于等于 (0) 的元素进行合并,这样做完以后相当于给这个元素乘 (2) ,并且不使前面一个元素的值增加了。但是按照这样的策略做不太好维护,考虑做完以后有许多块,除了第一个块以外每一个块都是负的,然后将这些块与第一个块合并。那么用并查集维护一下每个块,每一个元素被乘 (2) 的次数就是这个块里面位置比它小的元素个数。定义一个块的和为每个元素乘上其对应系数的和,对于一组询问,答案就是第一块的和加上 (2 imes) 其它块的和。
考虑怎么解决多组询问,将询问离线下来,对于每一个右端点 (r_i) ,如果询问的 (l_i=1) ,那么答案就是第一块的和加上 (2 imes) 其它块的和。否则找到 (l_i) 所在的块,这个块以 (l_i) 开头的后缀是对于这一询问的"第一个块",因为这个后缀任意时刻是非负的,所以这个后缀的块和就是从右到左直接合并的答案。那么只要维护一下块的前缀和以及一段区间进行从右到左直接合并的答案就好了。
感觉写的非常意识流,多手玩手玩就好了。
code
/*program by mangoyang*/
#include <bits/stdc++.h>
#pragma GCC optimize("Ofast", "inline")
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
int ch = 0, f = 0; x = 0;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
if(f) x = -x;
}
const int N = 1000005, mod = 1e9 + 7;
vector<pair<int, int> > Q[N];
int sz[N], sum[N], s[N], s2[N], ss[N], pw[N], fa[N], a[N], ans[N], n, q;
inline void up(int &x, int y){
x = x + y >= mod ? x + y - mod : x + y;
}
inline int Pow(int a, int b){
int ans = 1;
for(; b; b >>= 1, a = 1ll * a * a % mod)
if(b & 1) ans = 1ll * ans * a % mod;
return ans;
}
inline int ask(int x){
return x == fa[x] ? x : fa[x] = ask(fa[x]);
}
inline void unite(int x, int y){
int p = ask(x), q = ask(y);
if((sz[p] >= 30 && s[q]) || s[p] + (1ll * s[q] << sz[p]) >= mod) s[p] = mod;
else s[p] = s[p] + (1ll * s[q] << sz[p]);
up(s2[p], 1ll * s2[q] * pw[sz[p]] % mod);
fa[q] = p, sz[p] += sz[q];
}
inline int find(int x, int lim){
int l = 1, r = lim, res = lim + 1;
while(l <= r){
int mid = (l + r) >> 1;
if(ask(mid) > x) res = mid, r = mid - 1;
else l = mid + 1;
}
return res;
}
inline int gao(int x, int y){
return 1ll * (ss[y] - ss[x-1] + mod) % mod * Pow(Pow(2, x), mod - 2) % mod;
}
signed main(){
read(n), read(q), pw[0] = 1;
for(int i = 1; i <= n; i++){
read(a[i]), fa[i] = i, sz[i] = 1;
s[i] = a[i], s2[i] = (a[i] + mod) % mod;
pw[i] = 2ll * pw[i-1] % mod;
ss[i] = (ss[i-1] + 1ll * pw[i] * s2[i] % mod) % mod;
}
for(int i = 1, x, y; i <= q; i++)
read(x), read(y), Q[y].push_back(make_pair(x, i));
for(int i = 1; i <= n; i++){
while(s[ask(i)] >= 0 && ask(i) > 1) unite(ask(i) - 1, i);
sum[ask(i)] = (sum[ask(ask(i)-1)] + 2ll * s2[ask(i)] % mod) % mod;
for(int j = 0; j < (int) Q[i].size(); j++){
int x = Q[i][j].first, y = find(x, i);
int res = (sum[ask(i)] - sum[ask(y-1)] + mod) % mod;
ans[Q[i][j].second] = (res + gao(x, y - 1)) % mod;
}
}
for(int i = 1; i <= q; i++) printf("%d
", ans[i]);
return 0;
}