考虑离线做法。
对区间右端点排序。
考虑依次枚举区间右端点,并将扫到的新点加入线段树。
而对于最小值,其可以向左延伸,因此考虑扫一遍同时计算出能延伸到的最远的地方,这个单调栈就可以处理。那么算出来的这段区间即表示
a[i]
对这段区间产生了修改,覆盖信息线段树维护即可。
然后线段树还需要维护区间的答案,我们来看我们对线段树干了什么。
发现线段树总可以对x,维护x到右端点r间的最小值。而每次右端点右移,线段树对x,就改为维护x到右端点r+1间的最小值。发现实际上对于x,就是其子区间的不断扩充。容易想到我们在修改线段树的同时维护其历史值的和。那么每个点就可以维护以该点为区间左端点的所有区间最小值的和,那么我们此时的查询就是所有左端点维护的和的和。是线段树的查询。
问题解决。
并没有写莫队算法。。
让我们一起来膜Claris http://www.cnblogs.com/clrs97/p/4824806.html
BZOJ 4540
#include <cstdio>
#include <algorithm>
using namespace std;
#define FOR(i,j,k) for(i=j;i<=k;++i)
#define rep(i,j,k) for(i=j;i<k;++i)
const int N = 100005;
typedef long long ll;
int a[N], sk[N]; ll ans[N];
struct Ask {
int l, r, p;
Ask() { }
Ask(int _l, int _r, int _p)
: l(_l), r(_r), p(_p) { }
} b[N];
bool operator< (const Ask &a, const Ask &b) { return a.r < b.r; }
struct Tag {
ll a, b, c, d;
Tag() : a(1), b(0), c(0), d(0) { }
Tag(ll _a, ll _b, ll _c, ll _d) : a(_a), b(_b), c(_c), d(_d) { }
bool exists() { return a != 1 || b || c || d; }
Tag operator + (const Tag &B) {
return Tag(a * B.a, b * B.a + B.b, a * B.c + c, d + b * B.c + B.d);
}
} tmp;
struct Node {
ll v, s; int l; Tag t;
} T[N];
void add(int x, const Tag &p) {
T[x].s += p.c * T[x].v + p.d * T[x].l;
T[x].v = p.a * T[x].v + p.b * T[x].l;
T[x].t = T[x].t + p;
}
void pushdown(int t) {
if (T[t].t.exists()) {
add(t * 2, T[t].t);
add(t * 2 + 1, T[t].t);
T[t].t = Tag();
}
}
void update(int t) {
T[t].v = T[t * 2].v + T[t * 2 + 1].v;
T[t].s = T[t * 2].s + T[t * 2 + 1].s;
}
void build(int t, int l, int r) {
T[t].v = T[t].s = 0; T[t].l = r - l + 1, T[t].t = Tag();
if (l == r) return;
int mid = l + r >> 1;
build(t * 2, l, mid);
build(t * 2 + 1, mid + 1, r);
}
void modify(int t, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr) {
add(t, tmp); return;
}
pushdown(t);
int mid = l + r >> 1;
if (ql <= mid) modify(t * 2, l, mid, ql, qr);
if (qr > mid) modify(t * 2 + 1, mid + 1, r, ql, qr);
update(t);
}
ll query(int t, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr) return T[t].s;
pushdown(t);
int mid = l + r >> 1; ll ans = 0;
if (ql <= mid) ans += query(t * 2, l, mid, ql, qr);
if (qr > mid) ans += query(t * 2 + 1, mid + 1, r, ql, qr);
update(t);
return ans;
}
int main() {
int n, m, i, j = 0, l, r, cnt = 0, top = 0;
scanf("%d%d", &n, &m);
FOR(i,1,n) scanf("%d", &a[i]);
FOR(i,1,m) {
scanf("%d%d", &l, &r);
b[cnt++] = Ask(l, r, i, 1);
}
sort(b, b + cnt);
build(1, 1, n);
FOR(i,1,n) {
while (top && a[sk[top]] > a[i]) --top;
tmp = Tag(0, a[i], 0, 0), modify(1, 1, n, sk[top] + 1, i), add(1, Tag(1, 0, 1, 0));
for (; j < cnt && b[j].i == i; ++j)
ans[b[j].p] += query(1, 1, n, b[j].l, b[j].r);
sk[++top] = i;
}
FOR(i,1,m) printf("%lld\n", ans[i]);
return 0;
}
BZOJ 4248
#include <cstdio>
#include <algorithm>
using namespace std;
#define FOR(i,j,k) for(i=j;i<=k;++i)
#define rep(i,j,k) for(i=j;i<k;++i)
const int N = 300005, mod = 1000000000;
typedef long long ll;
int a[N], q[N], cnt; ll ans[N];
struct Ask {
int i, l, r, p, t;
Ask() { }
Ask(int _i, int _l, int _r, int _p, int _t)
: i(_i), l(_l), r(_r), p(_p), t(_t) { }
} b[N];
bool operator< (const Ask &a, const Ask &b) { return a.i < b.i; }
struct Tag {
ll a, b, c, d;
Tag() : a(1), b(0), c(0), d(0) { }
Tag(ll _a, ll _b, ll _c, ll _d) : a(_a), b(_b), c(_c), d(_d) { }
bool exists() { return a != 1 || b || c || d; }
Tag operator + (const Tag & B) {
return Tag(a * B.a, b * B.a + B.b, a * B.c + c, d + b * B.c + B.d);
}
} tmp;
struct Node {
ll v, s; int l; Tag t;
} T[N];
void add(int x, Tag p) {
T[x].v = p.a * T[x].v + p.b * T[x].l;
T[x].s += p.c * T[x].v + p.d * T[x].l;
T[x].t = T[x].t + p;
}
void pushdown(int t) {
if (T[t].t.exists()) {
add(t * 2, T[t].t);
add(t * 2 + 1, T[t].t);
T[t].t = Tag();
}
}
void update(int t) {
T[t].v = T[t * 2].v + T[t * 2 + 1].v;
T[t].s = T[t * 2].s + T[t * 2 + 1].s;
}
void build(int t, int l, int r) {
T[t].v = T[t].s = 0; T[t].l = r - l + 1, T[t].t = Tag();
if (l == r) return;
int mid = l + r >> 1;
build(t * 2, l, mid);
build(t * 2 + 1, mid + 1, r);
}
void modify(int t, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr) {
add(t, tmp); return;
}
pushdown(t);
int mid = l + r >> 1;
if (ql <= mid) modify(t * 2, l, mid, ql, qr);
if (qr > mid) modify(t * 2 + 1, mid + 1, r, ql, qr);
update(t);
}
ll query(int t, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr) return T[t].s;
pushdown(t);
int mid = l + r >> 1; ll ans = 0;
if (ql <= mid) ans += query(t * 2, l, mid, ql, qr);
if (qr > mid) ans += query(t * 2 + 1, mid + 1, r, ql, qr);
update(t);
return ans;
}
int main() {
int n, m, i, j, l1, r1, l2, r2, top = 0;
scanf("%d", &m);
rep(i,0,m) {
scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
if (l2 > 1) b[cnt++] = Ask(l2 - 1, l1, r1, i, -1);
b[cnt++] = Ask(r2, l1, r1, i, 1);
n = max(n, max(r1, r2));
}
sort(b, b + cnt);
ll t1 = 1023, t2 = 1025;
FOR(i,1,n) {
a[i] = t1 ^ t2;
t1 = t1 * 1023 % mod;
t2 = t2 * 1025 % mod;
}
build(1, 1, n); j = 0;
FOR(i,1,n) {
while (top && a[sk[top]] < a[i]) --top;
tmp = Tag(0, a[i], 0, 0), modify(1, 1, n, sk[top] + 1, i), add(1, Tag(1, 0, 1, 0));
for (; j < cnt && b[j].i == i; ++j)
ans[b[j].p] += query(1, 1, n, b[j].l, b[j].r) * b[j].t;
sk[++top] = i;
}
build(1, 1, n); j = 0;
FOR(i,1,n) {
while (top && a[sk[top]] > a[i]) --top;
tmp = Tag(0, a[i], 0, 0), modify(1, 1, n, sk[top] + 1, i), add(1, Tag(1, 0, 1, 0));
for (; j < cnt && b[j].i == i; ++j)
ans[b[j].p] -= query(1, 1, n, b[j].l, b[j].r) * b[j].t;
sk[++top] = i;
}
rep(i,0,m) printf("%lld\n", ans[i]);
return 0;
}
4540: [Hnoi2016]序列
Description
给定长度为n的序列:
a1,a2,⋯,an
,记为
a[1:n]
。类似地,
a[l:r](1≤l≤r≤N)
是指序列:
al,al+1,⋯,ar−1,ar
。若
1≤l≤s≤t≤r≤n
,则称
a[s:t]
是
a[l:r]
的子序列。现在有
q
个询问,每个询问给定两个数
6个子序列
a[1:1],a[2:2],a[3:3],a[1:2],a[2:3],a[1:3]
,这6个子序列的最小值之和为
5+2+4+2+2+2=17
。
Input
输入文件的第一行包含两个整数
n
和
,第
Output
对于每次询问,输出一行,代表询问的答案。
Sample Input
5 5
5 2 4 1 3
1 5
1 3
2 4
3 5
2 5
Sample Output
28
17
11
11
17
HINT
1≤n,q≤100000,|ai|≤109