【BZOJ4540】【Hnoi2016】序列 线段树

Claris劲啊!CA劲啊!

%%%两位线段树做法传送门在这里这里

逆向题解时间到:

首先将询问按照终点排序,并且一边从遍历,不妨设当前遍历到了点,对于之前的每个点,我们维护两个值。(之后的点的两个值都先设成0)

其中表示从这个点到之间序列A的最小值,而,表示从我们遍历第一个点到当前的所有时刻下的各个历史版本的和。(当遍历的点在这个点之前等于零)(事实上。)

不(很)难发现对于每一个询问,当且仅当时,有。因为

也就是说,如果我们把询问全部离线下来,遍历的时候可以快速更新并且求和,我们就可以快速回答每一个询问。

剩下就是线段树的锅了,我们把对于每个点的定义拓展到线段树的区间上,对于每个点以及它代表的,我们维护四个下传标记a,b,c,d,更新的时候,初始化时a=1,b=c=d=0.

每加入一个值,从这个数左边第一个大于它的数的位置+1到这个数的位置这一段的最小值都会被刷成,对应参数

同时我们要向加入最新版的的值,对应参数

两个下传标记打到一起时的处理方法来自于矩阵乘法。(图有毒,警告)

/**************************************************************
    Problem: 4540
    User: RicardoWang
    Language: C++
    Result: Accepted
    Time:5196 ms
    Memory:14948 kb
****************************************************************/

#include<cstdlib>
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
#define MAXN 100005
int size;
void _read(int &x)
{
    char ch=getchar(); bool flag=false; x=0;
    while(ch<'0' || ch>'9'){if(ch=='-')flag=true; ch=getchar();}
    while(ch>='0' && ch<='9'){x=x*10+ch-'0'; ch=getchar();} if(flag)x=-x; return ;
}
struct q
{
    int a,b,id;
}Q[MAXN];
int N,M,A[MAXN];
bool cmp(q x,q y)
{
    return x.b<y.b;
}
void Init()
{
    _read(N); _read(M);
    for(int i=1;i<=N;i++)_read(A[i]);
    for(int i=1;i<=M;i++){_read(Q[i].a); _read(Q[i].b);Q[i].id=i;}
    sort(Q+1,Q+1+M,cmp);
    return ;
}
struct node
{
    long long a,b,c,d;
    void clear(){a=1; b=c=d=0;}
    node friend operator + (node x,node y)
    {
        return (node){x.a*y.a,y.b+x.b*y.a,x.c+y.c*x.a,x.d+y.d+x.b*y.c};
    }
}w[2*MAXN];
int np,rt,chi[2*MAXN][2];
long long val[2*MAXN],sum[2*MAXN];
void build(int &now,int L,int R)
{
    now=++np; val[now]=sum[now]=0; w[now].clear();
    if(L==R) return ;
    int m=(L+R)>>1;
    build(chi[now][0],L,m); build(chi[now][1],m+1,R) ;return ;
}
void add(int now,int L,int R,node p)
{
    long long len=R-L+1;
    sum[now]+=p.c*val[now]+p.d*len;
    val[now]=p.a*val[now]+p.b*len;
    w[now]=w[now]+p;
    return ;
}
void push_down(int now,int L,int R)
{
    int m=(L+R)>>1;
    node t=w[now];
    if(t.a!=1 || t.b || t.c || t.d)
    {
        add(chi[now][0],L,m,t); add(chi[now][1],m+1,R,t);
        w[now].clear();
    }
    return ;
}
node tmp;
void update(int now,int L,int R,int x,int y)
{
    if(x<=L && R<=y){add(now,L,R,tmp);return ;}
    int m=(L+R)>>1;push_down(now,L,R);
    if(x<=m)update(chi[now][0],L,m,x,y);
    if(y>m)update(chi[now][1],m+1,R,x,y);
    sum[now]=sum[chi[now][0]]+sum[chi[now][1]]; val[now]=val[chi[now][0]]+val[chi[now][1]]; return ;
}
long long query(int now,int L,int R,int x,int y)
{
    if(x<=L && R<=y)return sum[now];
    int m=(L+R)>>1;push_down(now,L,R);
    if(y<=m) return query(chi[now][0],L,m,x,y);
    else if(x>m)return query(chi[now][1],m+1,R,x,y);
    else return query(chi[now][0],L,m,x,y)+query(chi[now][1],m+1,R,x,y);
}
int st[MAXN],top;
long long ans[MAXN];
char s[55];int cct;
void out(long long x)
{
    if(!x)putchar('0');
    if(x<0)putchar('-'),x=-x;
    cct=0;while(x){cct++; s[cct]=x%10+'0'; x=x/10;}
    while(cct){putchar(s[cct]); cct--;}
    putchar('\n');
}
void work()
{
    build(rt,1,N);
    top=0;int j=1;
    for(int i=1;i<=N;i++)
    {
        while(top&& A[st[top]]>=A[i])top--;
        tmp=(node){0,A[i],0,0}; update(rt,1,N,st[top]+1,i);
        add(1,1,N,(node){1,0,1,0}); st[++top]=i;
        while(j<=M && Q[j].b==i) {ans[Q[j].id]=query(rt,1,N,Q[j].a,Q[j].b);j++;}

    }
    for(int i=1;i<=M;i++)out(ans[i]);
    //out(0); out(-0); out(-123);out(1234);
    return ;
}
int main()
{
    //freopen("in.txt","r",stdin);
    Init();
    work();
    return 0;
}

(然而全程LL还不如莫队快)

时间: 2024-11-03 21:06:14

【BZOJ4540】【Hnoi2016】序列 线段树的相关文章

bzoj4540: [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个询问,每个询问给定两个数l和r,1≤l≤r≤n,求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

BZOJ 4034 树上操作(树的欧拉序列+线段树)

刷个清新的数据结构题爽一爽? 题意: 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a . 操作 3 :询问某个节点 x 到根的路径中所有点的点权和. 注意到操作3,询问x到根的路径之间点权和,容易发现这就是欧拉序列中的前缀和. 所以按照树的欧拉序列建线段树,然后操作1就变成单点修改,操作2,就变成了区间内某些点+a,某些点-a,也容易用tag标记

hdu45221——小明系列问题——小明序列 线段树优化dp

小明系列问题--小明序列 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others) Total Submission(s): 1918    Accepted Submission(s): 583 Problem Description 大家都知道小明最喜欢研究跟序列有关的问题了,可是也就因为这样,小明几乎已经玩遍各种序列问题了.可怜的小明苦苦地在各大网站上寻找着新的序列问题,可是找来找

BZOJ 1798 AHOI2009 Seq 维护序列 线段树

题目大意:维护一个序列,提供三种操作: 1.将区间中每个点的权值乘上一个数 2.将区间中每个点的权值加上一个数 3.求一段区间的和对p取模的值 2631的超^n级弱化版.写2631之前能够拿这个练练手... 线段树区间改动,让学校的大神指导了一下ZKW的区间改动方法,非常好理解,可是代码还是快不了. . . 回头再改改代码吧 可能是我写的太渣了 #include<cstdio> #include<cstring> #include<iostream> #include&

hdu 4521 小明系列问题——小明序列 线段树+二分

小明系列问题——小明序列 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others) Problem Description 大家都知道小明最喜欢研究跟序列有关的问题了,可是也就因为这样,小明几乎已经玩遍各种序列问题了.可怜的小明苦苦地在各大网站上寻找着新的序列问题,可是找来找去都是自己早已研究过的序列.小明想既然找不到,那就自己来发明一个新的序列问题吧!小明想啊想,终于想出了一个新的序列

hdu 4521 小明系列问题——小明序列(线段树+DP或扩展成经典的LIS)

小明系列问题--小明序列 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others) Total Submission(s): 1553    Accepted Submission(s): 457 Problem Description 大家都知道小明最喜欢研究跟序列有关的问题了,但是也就由于这样,小明差点儿已经玩遍各种序列问题了.可怜的小明苦苦地在各大站点上寻找着新的序列问题,但是找来

poj 2828 Buy Tickets 【买票插队找位置 输出最后的位置序列+线段树】

题目地址:http://poj.org/problem?id=2828 Sample Input 4 0 77 1 51 1 33 2 69 4 0 20523 1 19243 1 3890 0 31492 Sample Output 77 33 69 51 31492 20523 3890 19243 Hint The figure below shows how the Little Cat found out the final order of people in the queue d

CF380C Sereja and Brackets 括号序列+线段树

你可以手画一下,然后发现求的其实就是 $[l,r]$ 区间内合法序列匹配个数. 用线段树维护一下括号序列就可以了. code: #include <bits/stdc++.h> #define N 1000005 #define ll long long #define lson now<<1 #define rson now<<1|1 #define setIO(s) freopen(s".in","r",stdin) usin

HDU 4521 小明系列问题——小明序列 (线段树维护DP)

题目地址:HDU 4521 基本思路是DP.找前面数的最大值时能够用线段树来维护节省时间. 因为间隔要大于d. 所以能够用一个队列来延迟更新,来保证每次询问到的都是d个之前的. 代码例如以下: #include <iostream> #include <cstdio> #include <string> #include <cstring> #include <stdlib.h> #include <math.h> #include