P3246 [HNOI2016] 序列

P3246 [HNOI2016] 序列

Solution

可以将一个区间 \([l,r]\) 映射为平面上的一个点 $(l,r) $ ,令其权值为区间最小值,那么每次询问 \([l,r]\) 的答案就是求一个以 $(l,l) $ 为右上角以 \((r,r)\) 为右下角的矩阵的点权和

但是这里点权明显不可以一个个赋,所以考虑差分

用单调栈可以找出 \(a_i\) 左边第一个小于或等于 \(a_i\) 的 \(a_l\) ,同理可以找出右边第一个小于 \(a_i\) 的 \(a_r\) ,那么左上角 \((l+1,i)\) 右下角 \((i,r-1)\) 的

矩阵里每一点的点权都为 \(a_i\) ,这个是很方便用差分实现的,这样就把单点赋权转化为了区间修改

设 \(cf\) 为差分数组, \(b\) 为由差分还原后得到的平面, \(sum\) 为 \(b\) 的前缀和

那么每次询问的 \(ans = sum[r][r]+sum[l-1][l-1]-sum[l-1][r]-sum[r][l-1]\)

这样子就把一次询问拆解成了四次单点查询

考虑 \((i,j)\) 一点上的差分对 \(sum\) 的影响,可以发现 \((i,j)\) 上的差分会对其右下角任意一点 \((x,y)\) 贡献 \((x-i+1)(y-i+1)\) 次,即有

\[(x-i+1)(y-j+1){\times}cf[i][j] = cf[i][j]\times[(x+1)(y+1)-i(y+1)-j(x+1)+ij] \]

的贡献

那么就有

\[\begin{aligned} sum[x][y] &= \sum\limits_{i=1}^{x}\sum\limits_{j=1}^{y}cf[i][j]\times[(x+1)(y+1)-i(y+1)-j(x+1)+ij]\\ \end{aligned} \]

进一步地 可以把 \(sum[x][y]\) 拆成四项之和的形式,四项分别为

\[\begin{aligned} (x+1)(y+1)&\sum_{i=1}^{x}\sum_{j=1}^{y}cf[i][j]\\ &\sum_{i=1}^{x}\sum_{j=1}^{y}cf[i][j]\times ij\\ -(y+1)&\sum_{i=1}^{x}\sum_{j=1}^{y}cf[i][j]\times i\\ -(x+1)&\sum_{i=1}^{x}\sum_{j=1}^{y}cf[i][j]\times j \end{aligned} \]

于是可以把原题离线下来做,开四个树状数组分别维护式子中形如 \(\sum_{i=1}^{x}\sum_{j=1}^{y}cf[i][j]{\times}z\) 的那一部分

Code

#include <bits/stdc++.h>
#define rg register
#define il inline
#define int ll
using ll = long long;
using ull = unsigned long long;
using ld = long double;
using namespace std;
namespace myspace{
    const char endl = '\n';
    const int maxn = 1e5+50;
    il int read(){
        int x=0,f=1;
        char c = getchar();
        while(c<'0' || c>'9'){
            if(c == '-') f = -1;
            c = getchar();
        }
        while(c>='0' && c<='9'){
            x = x*10+c-'0';
            c = getchar();
        }
        return x*f;
    }
    il void write(int x){
        if(x<0){
            putchar('-');
            write(-x);
        } else if(x<10){
            putchar('0'+x);
        } else{
            write(x/10);
            putchar('0'+x%10);
        }
    }
    il void writeln(int x){
        write(x);
        putchar('\n');
    }
    int n,q;
    int a[maxn],l[maxn],r[maxn];
    int stk[maxn],top;//这个是单调栈
    il int lb(int x){
        return x&-x;
    }
    struct BIT{
        int tr[maxn];
        il int qry(int x){
            int res = 0;
            while(x){
                res += tr[x];
                x -= lb(x);
            }
            return res;
        }
        il void add(int x,int y){
            while(x<=n){
                tr[x] += y;
                x += lb(x);
            }
        }
    } tr1,tr2,tr3,tr4;
    struct action{
        int id;//操作id,对于一次查询操作即为询问id,修改操作统一为0
        int x,y;//操作点(x,y)的坐标
        int w;//操作数,即询问的系数或修改的差分值
        const friend bool operator<(const action & a,const action & b){
            if(a.y == b.y){
                return a.id<b.id;//让修改操作先于查询操作
            }
            return a.y<b.y;
        }
    };
    vector<action> act[maxn];
    int ans[maxn];
    signed main(){
        n = read();
        q = read();
        for(int i = 1;i<=n;i++){
            a[i] = read();
        }
        stk[0] = 0;
        top = 0;
        for(int i = 1;i<=n;i++){
            while(top && a[i]<a[stk[top]]){
                top--;
            }
            l[i] = stk[top];
            stk[++top] = i;
        }
        stk[0] = n+1;
        top = 0;
        for(int i = n;i>=1;i--){
            while(top && a[i]<=a[stk[top]]){
                top--;
            }
            r[i] = stk[top];
            stk[++top] = i;
        }
        for(int i = 1;i<=n;i++){
            int L = l[i];
            int R = r[i];
            act[L+1].push_back({0,L+1,i,a[i]});
            act[L+1].push_back({0,L+1,R,-a[i]});
            act[i+1].push_back({0,i+1,i,-a[i]});
            act[i+1].push_back({0,i+1,R,a[i]});
        }//操作点:修改
        for(int i = 1;i<=q;i++){
            int L = read(),R = read();
            act[R].push_back({i,R,R,1});
            act[L-1].push_back({i,L-1,R,-1});
            act[R].push_back({i,R,L-1,-1});
            act[L-1].push_back({i,L-1,L-1,1});
        }//操作点:查询
        for(int i = 1;i<=n;i++){
            for(auto[id,x,y,w] : act[i]){
                if(id == 0){
                    tr1.add(y,w);
                    tr2.add(y,w*x*y);
                    tr3.add(y,w*x);
                    tr4.add(y,w*y);
                } else{
                    ans[id] += w*(x+1)*(y+1)*tr1.qry(y);
                    ans[id] += w*tr2.qry(y);
                    ans[id] += -w*(y+1)*tr3.qry(y);
                    ans[id] += -w*(x+1)*tr4.qry(y);
                }
            }
        }
        for(int i = 1;i<=q;i++){
            writeln(ans[i]);
        }
        return 0;
    }
}
signed main(){
    #ifdef ONLINE_JUDGE
    #else
        freopen("in.in","r",stdin);
        freopen("out.out","w",stdout);
    #endif
        myspace::main();
        return 0;
}
posted @ 2026-08-10 13:40  _2e21  阅读(7)  评论(0)    收藏  举报