BZOJ 4540|HNOI 2016|序列|线段树|离线

75 篇文章 1 订阅
27 篇文章 0 订阅

考虑离线做法。
对区间右端点排序。
考虑依次枚举区间右端点,并将扫到的新点加入线段树。

而对于最小值,其可以向左延伸,因此考虑扫一遍同时计算出能延伸到的最远的地方,这个单调栈就可以处理。那么算出来的这段区间即表示 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](1lrN) 是指序列: al,al+1,,ar1,ar 。若 1lstrn ,则称 a[s:t] a[l:r] 的子序列。现在有 q 个询问,每个询问给定两个数l r 1lrn,求 a[l:r] 的不同子序列的最小值之和。例如,给定序列 5,2,4,1,3 ,询问给定的两个数为1和3,那么 a[1:3]
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 q,分别代表序列长度和询问数。接下来一行,包含 n 个整数,以空格隔开
,第i个整数为 ai ,即序列第 i 个元素的值。接下来q行,每行包含两个整数 l r,代表一次询问。

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

1n,q100000,|ai|109

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值