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

    只有询问,允许离线,我们可以考虑使用莫队。

    那么问题在于我们怎么计算右端点 (+1) 带来的增量,可以发现这些新增加的子区间都是以 (r) 为右端点的,那么我们只需要考虑以 (r) 为右端点,左端点在 ([l, r]) 的区间最小值之和,可以发现这个的答案是可以转移的,于是我们令 (dp_{l, r}) 为上面这个最小值之和,那么就有转移:

    [dp_{l, r} = dp_{l, {li}_i} + (i - {li}_i) imes a_i ]

    ({li}_i)(i) 左边第一个比 (i) 小的位置,这个可以使用单调栈求出。可以发现上面那个 (dp) 的边界应该是 (dp_{l, p} = a_p imes (p - l + 1)),其中 (p)([l, r]) 中最小值所在的位置。但是上面这个 (dp) 对于每个 (l) 是不一样的,我们显然不能对每个 (l) 预处理出每个 (r) 的答案,但是仔细分析可以发现不论 (l) 是什么每次我们转移到的地方和转移产生的增量都只与 (i) 有关是个定值,并且我们最后肯定是转移到某个位置 (x),然后再转移到 (p) 结束转移,可以发现我们要求的 (dp_{l, r} = a_p imes (p - l + 1) + a_x imes(x - p) + cdots + a_r imes (r - {li}_r)),而对于每个区间 (a_p imes (p - l + 1)) 是已知的,我们所需要知道的实际上是后一部分 (a_x imes(x - p) + cdots + a_r imes (r - {li}_r)) 增量的和,因为不论 (l) 是什么中间的增量都是一定的,那么我们何不将 (l) 设置为 (1),那么中间的这部分增量就可以表示成 (dp_{1, r} - dp_{1, p}) 了。总地来说,我们预处理出 (f_i) 表示以 (i) 为右端点,左端点在 ([1, i]) 的答案,那么以 (r) 为右端点,左端点在 ([l, r]) 中的答案就可以表示为 (f_r - f_p + a_p imes (p - l + 1)),这样我们就解决了右端点 (+1) 时对答案的贡献,考虑左端点移动时对答案的贡献只需将序列翻转过来看待即可。

    一些坑点

    • 我们先要扩大区间再减小区间,因为如果中途出现了 (l > r) 会导致答案计算错误。

    • (st) 表倍增预处理时最好精确到最大的 (log),并且将第一维空间开两倍或者直接在 (i + (1 << (j - 1))) 时特判。

    #include<bits/stdc++.h>
    using namespace std;
    #define N 100000 + 5
    #define M 20 + 5
    #define rep(i, l, r) for(int i = l; i <= r; ++i)
    #define dep(i, l, r) for(int i = r; i >= l; --i)
    struct node{
        int l, r, p;
    }q[N];
    long long tmp, ans[N], pre[N], suf[N];
    int n, l, r, Q, top, size, a[N], li[N], ri[N], st[N], block[N], f[3 * N][M];
    int read(){
        char c; int x = 0, f = 1;
        c = getchar();
        while(c > '9' || c < '0'){ if(c == '-') f = -1; c = getchar();}
        while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
        return x * f;
    }
    bool cmp(node a, node b){
        return block[a.l] == block[b.l] ? ((block[a.l] & 1) ? a.r < b.r : a.r > b.r) : a.l < b.l;
    }
    int query(int l, int r){
        int D = log2(r - l + 1);
        return a[f[l][D]] < a[f[r - (1 << D) + 1][D]] ? f[l][D] : f[r - (1 << D) + 1][D];
    }
    void updatel(int p, int w){
        int mxp = query(p, r);
        tmp += 1ll * w * (suf[p] - suf[mxp] + 1ll * a[mxp] * (r - mxp + 1)); 
    }
    void updater(int p, int w){
        int mxp = query(l, p);
        tmp += 1ll * w * (pre[p] - pre[mxp] + 1ll * a[mxp] * (mxp - l + 1)); 
    }
    signed main(){
        n = read(), Q = read(), size = sqrt(n); 
        rep(i, 1, n) a[i] = read(), f[i][0] = i, block[i] = ceil(1.0 * i / size);
        rep(j, 1, 17) rep(i, 1, n){
            if(a[f[i][j - 1]] < a[f[i + (1 << (j - 1))][j - 1]]) f[i][j] = f[i][j - 1];
            else f[i][j] = f[i + (1 << (j - 1))][j - 1];
        }
        dep(i, 1, n){
            while(top && a[st[top]] > a[i]) li[st[top]] = i, --top;
            st[++top] = i;
        }
        top = 0;
        rep(i, 1, n){
            while(top && a[st[top]] > a[i]) ri[st[top]] = i, --top;
            st[++top] = i;
        }
        rep(i, 1, n) pre[i] = pre[li[i]] + 1ll * (i - li[i]) * a[i];
        dep(i, 1, n) suf[i] = suf[ri[i]] + 1ll * (ri[i] - i) * a[i];
        rep(i, 1, Q) q[i].l = read(), q[i].r = read(), q[i].p = i;
        sort(q + 1, q + Q + 1, cmp);
        l = 1, r = 0;
        rep(i, 1, Q){
            while(r < q[i].r) updater(++r, 1);
            while(l > q[i].l) updatel(--l, 1);
            while(r > q[i].r) updater(r--, -1);
            while(l < q[i].l) updatel(l++, -1);
            ans[q[i].p] = tmp;
        }
        rep(i, 1, Q) printf("%lld
    ", ans[i]);
        return 0;
    }
    

    可以发现上面那个 (dp) 本身是不依赖于莫队的,有没有一种更为简单的方法呢?实际上是有的,可以发现对于一次询问我们实际上是在求这个式子:

    [sumlimits_{i = l} ^ r dp_{l, i} ]

    因为我们已经知道 (dp_{p + 1, i}) 怎么算,不妨将上式的贡献来源拆分成三个部分:区间跨过 (p) 的贡献,即 (a_p imes (p - l + 1) imes (r - p + 1));两个端点均在 (p) 右边的情况,即 (sumlimits_{i = p + 1} ^ r dp_{p + 1, i} = sumlimits_{i = p + 1} ^ r f_i - f_p) 于是只需记录 (pre_i)(f_j) 的前缀和即可;两个端点均在 (p) 左边的情况,同理于上一种情况,将序列翻转来看即可。于是我们可以得到如下代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define N 100000 + 5
    #define M 20 + 5
    #define rep(i, l, r) for(int i = l; i <= r; ++i)
    #define dep(i, l, r) for(int i = r; i >= l; --i)
    unsigned long long S, A, B, C, ans, lastans = 0, f[N], g[N], pre[N], suf[N];
    int n, l, r, Q, top, type, a[N], li[N], ri[N], st[N], dp[3 * N][M];
    int read(){
        char c; int x = 0, f = 1;
        c = getchar();
        while(c > '9' || c < '0'){ if(c == '-') f = -1; c = getchar();}
        while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
        return x * f;
    }
    unsigned long long Random(){
    	return S ^= (A + B * lastans) % C;
    }
    int query(int l, int r){
        int D = log2(r - l + 1);
        return a[dp[l][D]] < a[dp[r - (1 << D) + 1][D]] ? dp[l][D] : dp[r - (1 << D) + 1][D];
    }
    signed main(){
        n = read(), Q = read(), type = read();
        rep(i, 1, n) a[i] = read(), dp[i][0] = i;
        rep(j, 1, 17) rep(i, 1, n){
            if(a[dp[i][j - 1]] < a[dp[i + (1 << (j - 1))][j - 1]]) dp[i][j] = dp[i][j - 1];
            else dp[i][j] = dp[i + (1 << (j - 1))][j - 1];
        }
        dep(i, 1, n){
            while(top && a[st[top]] > a[i]) li[st[top]] = i, --top;
            st[++top] = i;
        }
        top = 0;
        rep(i, 1, n){
            while(top && a[st[top]] > a[i]) ri[st[top]] = i, --top;
            st[++top] = i;
        }
        rep(i, 1, n) f[i] = f[li[i]] + 1ull * (i - li[i]) * a[i], pre[i] = pre[i - 1] + f[i];
        dep(i, 1, n) g[i] = g[ri[i]] + 1ull * (ri[i] - i) * a[i], suf[i] = suf[i + 1] + g[i];
        if(type) S = read(), A = read(), B = read(), C = read();
        while(Q--){
            if(!type) l = read(), r = read();
            else{ l = Random() % n + 1, r = Random() % n + 1; if(l > r) swap(l, r);}
            int p = query(l, r);
            lastans = pre[r] - pre[p - 1] - f[p] * (r - p + 1);
            lastans += suf[l] - suf[p] - g[p] * (p - l);
            lastans += 1ull * (r - p + 1) * (p - l + 1) * a[p];
            ans ^= lastans;
        }
        printf("%llu", ans);
        return 0;
    }
    
    GO!
  • 相关阅读:
    [原]three.js 地形法向量生成
    C# 创建XML文档
    <转载>在C#中操作XML(基础操作)
    <转载>Visual C#.NetSocket篇
    <转载>批处理重定向中的秘密
    <转载>最基本的Socket编程C#版
    <转载>在.NET中运行外部程序的3种方法
    <转载>修改Win7远程桌面端口
    <转载>Visual C#.NetTCP篇
    <转载>C#中的委托和事件(续)
  • 原文地址:https://www.cnblogs.com/Go7338395/p/13485301.html
Copyright © 2011-2022 走看看