zoukankan      html  css  js  c++  java
  • [HNOI2016] 序列

    题目链接:

    序列

    题目分析:

    看到询问跳来跳去,各个询问之间的计算又有重叠部分,考虑能不能莫队一下
    麻烦的是如何(O(1))(l,r)指针挪动一格的时候更新答案
    以考虑挪动右指针为例
    ([l, r] ightarrow [l, r + 1])
    新产生的区间是所有以(r + 1)为右端点,左端点在([l, r + 1])内的所有区间
    然后我们发现最小值是一段一段出现的
    举个例子
    2 1 6 3 4 5
    假设我们固定右端点,把左端点从1挪到5,那么对应每个区间的最小值就是
    1 1 3 3 4 5
    可以发现不仅成段而且还递增,这个是易证的
    考虑能不能预处理出每一段的起始结束位置,计算贡献就可以用((最小值覆盖区间长度)*当前最小值)来计算了
    有一个常用的(trick)是处理出每个数前面第一个比它小的数(pre_i),这个可以用单调栈做到(O(n))
    然后可以得到一个式子,其中(f_i)表示右端点钦定为(i),左端点位于(1)(i)的所有区间的最小值之和
    (f_i = f_{pre_i} + (i - pre_i) * a[i])
    即当左端点在(pre_i)前面的时候,我们直接把(pre_i)的答案继承过来,然后加上这一段的新答案,即(pre_i)(i)这一段都是以(a_i)为最小值的贡献
    这个东西感性理解上是可减的,洛谷题解有一篇我觉得比较有道理,从容斥的角度去理解
    把这个东西预处理出来,然后每次右指针右挪时候先查找([l, r+1])内的最小值位置(pos),这样左端点在([l, pos])内的单个区间贡献都是(a_{pos}),剩下的可以用(f)得到
    (ans = (pos - l + 1) * a[pos] + f[r + 1] - f[pos])
    减的话就是乘一个(-1)的系数,左边的对称处理一下就好

    代码:

    代码跑得很慢,我也不知道为什么,可能这个莫队假了吧……得吸氧才能过

    #include<bits/stdc++.h>
    //#define int long long
    using namespace std;
    inline int read() {
    	int cnt = 0, f = 1; char c = getchar();
    	while (!isdigit(c)) {if (c == '-') f = -f; c = getchar();}
    	while (isdigit(c)) {cnt = (cnt << 3) + (cnt << 1) + (c ^ 48); c = getchar();}
    	return cnt * f;
    }
    const int N = (int)2e5 + 5;
    typedef long long ll;
    int n, m, pos[N];
    ll ans[N], res, a[N];
    struct Query {
    	int l, r, id;
    } Q[N];
    bool cmp(Query a, Query b) {
    	return pos[a.l] == pos[b.l] ? a.r < b.r : pos[a.l] < pos[b.l];
    }
    int ST[22][N], g[22][N];
    void ST_prework() {
    	for (register int i = 1; i <= n; ++i) ST[0][i] = a[i], g[0][i] = i;
    	int t = log(n) / log(2);
    	for (register int j = 1; j <= t; ++j) 
    		for (register int i = 1; i <= n - (1 << j) + 1; ++i) {
    			ST[j][i] = ST[j - 1][i] < ST[j - 1][i + (1 << (j - 1))] ? ST[j - 1][i] : ST[j - 1][i + (1 << (j - 1))];
    			g[j][i] = ST[j - 1][i] < ST[j - 1][i + (1 << (j - 1))] ? g[j - 1][i] : g[j - 1][i + (1 << (j - 1))];
    		}
    }
    int query(int L, int R) {
    	int k = log(R - L + 1) / log(2);
    	return ST[k][L] < ST[k][R - (1 << k) + 1] ? g[k][L] : g[k][R - (1 << k) + 1];
    }
    int sta[N], top, pre[N], suf[N], l, r;
    ll f1[N], f2[N];
    void prework() {
    	for (register int i = 1; i <= n; ++i) pre[i] = 0, suf[i] = n + 1;
    	for (register int i = 1; i <= n; ++i) {
    		while (a[sta[top]] > a[i] && top) --top;
    		pre[i] = sta[top];
    		sta[++top] = i;
    	}
    	for (register int i = 1; i <= n; ++i) 
    		f1[i] = f1[pre[i]] + a[i] * (i - pre[i]);
    	top = 0;
    	for (register int i = n; i >= 1; --i) {
    		while (a[sta[top]] > a[i] && top) --top;
    		suf[i] = sta[top];
    		sta[++top] = i;
    	}
    	for (register int i = n; i >= 1; --i) 
    		f2[i] = f2[suf[i]] + a[i] * (suf[i] - i);
    }
    void modify1(int L, int R, int k) {
    	int p = query(L, R);
    	res += k * ((R - p + 1) * a[p] + f2[L] - f2[p]);
    }
    void modify2(int L, int R, int k) {
    	int p = query(L, R);
    	res += k * ((p - L + 1) * a[p] + f1[R] - f1[p]);
    }
    signed main() {
    //	freopen("1.in", "r", stdin);
    	n = read(), m = read();
    	for (register int i = 1; i <= n; ++i) a[i] = read();
    	a[0] = a[n + 1] = (int)1e9 + 7;
    	ST_prework(); prework();
    	for (register int i = 1; i <= m; ++i) 
    		Q[i].l = read(), Q[i].r = read(), Q[i].id = i;
    	int base = sqrt(m);
    	for (register int i = 1; i <= m; ++i) pos[Q[i].l] = Q[i].l / base;
    	sort (Q + 1, Q + m + 1, cmp);
    	l = 1, r = 1, res = 0;
    	res += a[1];
    	for (register int i = 1; i <= m; ++i) {
    		while (l > Q[i].l) modify1(--l, r, 1);
    		while (r < Q[i].r) modify2(l, ++r, 1);
    		while (l < Q[i].l) modify1(l++, r, -1);
    		while (r > Q[i].r) modify2(l, r--, -1);
    		ans[Q[i].id] = res;
    	}
    	for (register int i = 1; i <= m; ++i) printf("%lld
    ", ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    pair
    非整除集合
    集合 set
    实现字通配符*
    vector
    矩阵及其初等变换
    求数组中连续子数组(最少有一个元素)的最大和。
    最长上升序列(Lis)
    st表求区间最大值
    [Noip2015] 信息传递
  • 原文地址:https://www.cnblogs.com/kma093/p/12038001.html
Copyright © 2011-2022 走看看