BZOJ 4540 [Hnoi2016]序列

线段树+矩阵

我来发一篇博客证明我还活着

题目要求的区间所有子区间的最小值之和不太容易合并,因此直接上数据结构不好做。

考虑离线做法。把所有询问右端点排序。

从左到右扫描整个序列,扫描到 i 时维护所有1ji的区间 [j,i] 的答案。那每做完一个 i 只要抓出当前询问的区间查询即可。

如何随着i右移,动态地维护前缀所有区间的答案?新加一个 i 进来的时候,只会增加右端点在i的子区间。这些新区间的最小值要么是 i1 时候的最小值即不变,要么就是 a[i] 。开线段树搞一搞,注意到都是线性变换,矩阵维护即可。

然后大概卡了一个中午的常数吧。

#include<cstdio>
#include<vector>
#include<cstring>
#define N 100005
using namespace std;
int read()
{
    int r = 0, p = 0; char c = getchar();
    for(; c < '0' || c > '9'; c = getchar()) c == '-' ? p = 1 : 0;
    for(; c >='0' && c <='9'; r = r*10+c-'0', c = getchar());
    return p?-r:r;
}
namespace runzhe2000
{
    typedef long long ll;
    const int INF =  1<<30;
    int n, q, a[N], sta[N], stacnt, nocnt;
    ll ans[N];
    struct que{int id, l; que *next;}node[N], *last[N];
    struct matrix
    {
        ll a[3][3];
        matrix(){memset(a,0,sizeof(a));}
        matrix operator * (matrix &that)
        {
            matrix r;
            for(int i = 0; i < 3; i++)
            {
                ll *rai = r.a[i], *ai = a[i];
                for(int j = 0; j < 3; j++)
                    for(int k = 0; k < 3; k++) 
                        rai[j] += ai[k]*that.a[k][j];
            }
            return r;
        }
    }I, mat_v, mat_sum;

    struct seg
    {
        matrix val, tag;
        int need;
    }t[N*4];
    void pushdown(int x)
    {
        if(!t[x].need)return; t[x].need = 0;
        seg *ls = &t[x<<1], *rs = &t[x<<1|1];

        ls->val = ls->val * t[x].tag;
        ls->tag = ls->tag * t[x].tag;

        rs->val = rs->val * t[x].tag;
        rs->tag = rs->tag * t[x].tag;

        ls->need = rs->need = 1;
        t[x].tag = I;
    }
    void pushup(int x)
    {
        ll *a0 = t[x].val.a[0], *a0l = t[x<<1].val.a[0], *a0r = t[x<<1|1].val.a[0];
        for(int i = 0; i < 3; i++) a0[i] = a0l[i] + a0r[i];
    }
    void build(int x, int l, int r)
    {
        t[x].val.a[0][2] = r-l+1; t[x].tag = I; t[x].need = 0;
        if(l == r) return; int mid = (l+r)>>1;
        build(x<<1,l,mid); build(x<<1|1,mid+1,r);
    }
    void modi(int x, int l, int r, int ql, int qr, matrix &val)
    {
        if(ql <= l && r <= qr)
        {
            t[x].need = 1;
            t[x].tag = t[x].tag * val;
            t[x].val = t[x].val * val;
            return;
        }
        pushdown(x);
        int mid = (l+r)>>1; 
        if(ql <= mid) modi(x<<1,l,mid,ql,qr,val);
        if(mid <  qr) modi(x<<1|1,mid+1,r,ql,qr,val);
        pushup(x);
    }
    ll query(int x, int l, int r, int ql, int qr)
    {
        if(ql <= l && r <= qr) return t[x].val.a[0][0]; 
        int mid = (l+r)>>1; ll ret = 0;
        pushdown(x);
        if(ql <= mid) ret += query(x<<1,l,mid,ql,qr);
        if(mid <  qr) ret += query(x<<1|1,mid+1,r,ql,qr);
        return ret;
    }
    void main()
    {
        n = read(), q = read();
        for(int i = 1; i <= n; i++) a[i] = read();

        for(int i = 0; i < 3; i++) I.a[i][i] = mat_sum.a[i][i] = 1;
        mat_sum.a[1][0] = 1;
        mat_v.a[0][0] = mat_v.a[2][2] = 1;

        build(1,1,n);
        for(int i = 1; i <= q; i++)
        {
            int l = read(), r = read();
            node[++nocnt] = (que){i,l,last[r]};
            last[r] = &node[nocnt];
        }
        a[0] = -INF;
        for(int i = 1; i <= n; i++)
        {
            for(; a[sta[stacnt]] >= a[i]; stacnt--);
            mat_v.a[2][1] = a[i];
            modi(1,1,n,sta[stacnt]+1,i,mat_v);
            modi(1,1,n,1,i,mat_sum);
            sta[++stacnt] = i;
            for(que *j = last[i]; j; j = j->next)
            {
                ans[j->id] = query(1,1,n,j->l,i);
            }
        }
        for(int i = 1; i <= q; i++) printf("%lld\n",ans[i]);
    }
}
int main()
{
    runzhe2000::main();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值