bzoj 4540 [HNOI 2016] 序列 - 莫队算法 - Sparse-Table - 单调栈

题目传送门

  传送点I

  传送点II

题目大意

  给定一个长度为$n$的序列。询问区间$[l, r]$的所有不同的子序列的最小值的和。

  这里的子序列是连续的。两个子序列不同当且仅当它们的左端点或右端点不同。

  不会直接上神奇数据结构的做法。

  考虑莫队。当在一段右边加入一个数后,考虑它产生的贡献。

  首先找到加入后这一段的最小值,那么左端点在它的左侧的时候这个最小值做出贡献。

  对于它右边到新加入的数新造成的贡献用同样的方法计算,期望下多带个$log$,然后题目没说数据随机。

  考虑右边这一部分其实被算重了许多次,因为最小值右边的分段情况比较固定。因为每个"最小值"有一个固定的“恶势力”范围。这个东西可以用前缀和再加上单调栈预处理。

  求最小的任务就交给st表好了。

Code

  1 /**
  2  * bzoj
  3  * Problem#4540
  4  * Accepted
  5  * Time: 10024ms
  6  * Memory: 34932k 
  7  */
  8 #include <algorithm>
  9 #include <iostream>
 10 #include <cstdlib>
 11 #include <cstdio>
 12 #ifndef WIN32
 13 #define Auto "%lld"
 14 #else
 15 #define Auto "%I64d"
 16 #endif
 17 using namespace std;
 18 typedef bool boolean;
 19 
 20 const signed int inf = (signed) (~0u >> 1);
 21 const int N = 1e5 + 5, bzmax = 18;
 22 #define pii pair<int, int>
 23 #define fi first
 24 #define sc second
 25 
 26 typedef class SparseTable {
 27     public:
 28         int n;
 29         int *ar;
 30         int log2[N];
 31         pii bz[N][bzmax];
 32 
 33         SparseTable() {    }
 34         SparseTable(int n, int* ar):n(n) {
 35             log2[1] = 0;
 36             for (int i = 2; i <= n; i++)
 37                 log2[i] = log2[i >> 1] + 1;
 38             for (int i = 1; i < n; i++)
 39                 bz[i][0] = min(pii(ar[i], i), pii(ar[i + 1], i + 1));
 40             for (int j = 1; j < bzmax; j++)
 41                 for (int i = 1; i + (1 << j) <= n + 1; i++)
 42                     bz[i][j] = min(bz[i][j - 1], bz[i + (1 << (j - 1))][j - 1]);
 43         }
 44 
 45         int query(int l, int r) {
 46             if (l == r)    return l;
 47             int l2 = log2[r - l];
 48             return min(bz[l][l2], bz[r - (1 << l2)][l2]).sc;
 49         }
 50 }SparseTable;
 51 
 52 const int cs = 350;
 53 #define ll long long
 54 
 55 typedef class Query {
 56     public:
 57         int l, r, id;
 58         ll res;
 59 
 60         Query() { }
 61 
 62         boolean operator < (Query b) const {
 63             if (l / cs != b.l / cs)    return l / cs < b.l / cs;
 64             return r < b.r;
 65         }
 66 }Query;
 67 
 68 int n, m;
 69 int tp = -1;
 70 Query *qs;
 71 SparseTable st;
 72 ll *pl, *pr;
 73 int *ar;
 74 pii *sta;
 75 
 76 inline void init() {
 77     scanf("%d%d", &n, &m);
 78     pl = new ll[(n + 1)];
 79     pr = new ll[(n + 2)];
 80     ar = new int[(n + 1)];
 81     sta = new pii[(n + 1)];
 82     qs = new Query[(m + 1)];
 83     for (int i = 1; i <= n; i++)
 84         scanf("%d", ar + i);
 85     st = SparseTable(n, ar);
 86     for (int i = 1; i <= m; i++)
 87         scanf("%d%d", &qs[i].l, &qs[i].r), qs[i].id = i;
 88 }
 89 
 90 inline void prepare() {
 91     sta[++tp] = pii(0, -inf);
 92     pl[0] = 0, pr[n + 1] = 0;
 93     for (int i = 1; i <= n; i++) {
 94         while (tp && sta[tp].sc >= ar[i])
 95             tp--;
 96         pl[i] = pl[sta[tp].fi] + (i - sta[tp].fi) * 1ll * ar[i];
 97         sta[++tp] = pii(i, ar[i]);
 98     }
 99 
100     sta[tp = 0] = pii(n + 1, -inf);
101     for (int i = n; i; i--) {
102         while (tp && sta[tp].sc >= ar[i])
103             tp--;
104         pr[i] = pr[sta[tp].fi] + (sta[tp].fi - i) * 1ll * ar[i];
105         sta[++tp] = pii(i, ar[i]);
106     }
107 }
108 
109 ll res = 0;
110 
111 inline void solve() {
112     sort(qs + 1, qs + m + 1);
113     int c = 1;
114     for (int sid = 0, p; sid <= n / cs && c <= m; sid++) {
115         int mdzzl = 1, mdzzr = 0;
116         res = 0;
117         for ( ; c <= m && qs[c].l / cs == sid; c++) {
118             while (mdzzr < qs[c].r) {
119                 p = st.query(mdzzl, ++mdzzr);
120                 res += (p - mdzzl + 1) * 1ll * ar[p] + pl[mdzzr] - pl[p];
121             }
122             while (mdzzl < qs[c].l) {
123                 p = st.query(mdzzl, mdzzr);
124                 res -= (mdzzr - p + 1) * 1ll * ar[p] + pr[mdzzl] - pr[p];
125                 mdzzl++;
126             }
127             while (mdzzl > qs[c].l) {
128                 p = st.query(--mdzzl, mdzzr);
129                 res += (mdzzr - p + 1) * 1ll * ar[p] + pr[mdzzl] - pr[p];
130             }
131             qs[c].res = res;
132         }
133     }
134     for (int i = 1; i <= m; i++)
135         while (qs[i].id != i)
136             swap(qs[i], qs[qs[i].id]);
137     for (int i = 1; i <= m; i++)
138         printf(Auto"\n", qs[i].res);
139 }
140 
141 int main() {
142     init();
143     prepare();
144     solve();
145     return 0;
146 }

转载于:https://www.cnblogs.com/yyf0309/p/9393102.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值