zoukankan      html  css  js  c++  java
  • Codeforces 878 E. Numbers on the blackboard

    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;
    }
    
  • 相关阅读:
    每天写点shell——read的用法
    串口编程(一) :理论基础
    常用算法3
    Linux man C++ 库函数
    网络爬虫(一):配置selenium、pycharm(windows平台)
    C/C++ 类型内存占用详解
    git常用命令总结
    常用算法2
    本地git关联远程github
    python实现float/double的0x转化
  • 原文地址:https://www.cnblogs.com/mangoyang/p/11732884.html
Copyright © 2011-2022 走看看