[BZOJ4540]序列

给出一个序列,求某段区间的位置不同子串的最小值之和

对于前$40%$,我们可以$n^{2}$预处然后$O(n\sqrt{n})$

预处理中$zzz[i][j]$表示以$i$开头,结尾最长到$j$的前缀子串的最小值之和

同理$fff[i][j]$表示倒过来的情况

考虑对预处理进行优化

对于一个区间$[l,r]$,以$l$开头,结尾最长到$r$的前缀可分为两部分计算,

设$x$为区间$[l,r]$中最小值所在位置

对于结尾在区间$[x,r]$中的子串,对答案的贡献为$A[x]*(r-x+1)$

对于结尾在区间$[l,x)$中的子串,我们先找到下一个比$A[l]$小的位置$p1$,则对于结尾在区间$[l,p1)$中的子串,对答案的贡献为$A[l]*(p1-l)$,再找到下一个比$A[p1]$小的位置$p2$类推,直到找到$x$为止

这就相当于对于一个数$A[x]$,我们把下一个比它小的数$A[y]$向它连边,边权为$A[x]*(y-x)$,然后求树上的节点$l$到$x$的距离和

这个过程我们可以通过维护单调栈预处理出来

$zzz[i]$表示节点到$i$所属的树根的距离,因为可能是森林

从后往前枚举,当前值小于等于栈顶时弹栈,然后$zzz[i]=zzz[sta[top]]+A[i]*(sta[top]-i)$

$fff[i]$同理

然后莫队,,,复杂度$O(nlogn+n\sqrt{n})$

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define inf 0x3f3f3f3f
 4 #define maxn 100005
 5 typedef long long ll;
 6 struct node{ int l,r,blc,id; ll ans; }q[maxn];
 7 int n,m,sta[maxn],mn[maxn<<1][20],log_2[maxn];
 8 ll A[maxn],fff[maxn],zzz[maxn];
 9 bool cmp1(node a,node b){
10     if(a.blc!=b.blc)return a.blc<b.blc;
11     else if(a.r!=b.r)return a.r<b.r;
12     else return a.l<b.l;
13 }
14 bool cmp2(node a,node b){ return a.id<b.id; }
15 void PP(){
16     int top=0;
17     for(int i=1;i<=n;i++){
18         while(top&&A[i]<=A[sta[top]])top--;
19         fff[i]=fff[sta[top]]+A[i]*(i-sta[top]);
20         sta[++top]=i;
21     }
22     sta[top=0]=n+1;
23     for(int i=n;i;i--){
24         while(top&&A[i]<=A[sta[top]])top--;
25         zzz[i]=zzz[sta[top]]+A[i]*(sta[top]-i);
26         sta[++top]=i;
27     }
28 }
29 int Min(int x,int y){
30     return A[x]<A[y]?x:y;
31 }
32 void build_ST(){
33     A[0]=inf;
34     for(int i=1;i<=n;i++)
35         mn[i][0]=i;
36     log_2[1]=0;
37     for(int i=2;i<=n;i++)
38         log_2[i]=log_2[i>>1]+1;
39     for(int j=1;j<=log_2[n];j++)
40         for(int i=1;i<=n;i++)
41             mn[i][j]=Min(mn[i][j-1],mn[i+(1<<(j-1))][j-1]);
42 }
43 int query_ST(int l,int r){
44     int len=log_2[r-l+1];
45     return Min(mn[l][len],mn[r-(1<<len)+1][len]);
46 }
47 ll call(int l,int r){
48     int x=query_ST(l,r);
49     return A[x]*(r-x+1)+zzz[l]-zzz[x];
50 }
51 ll calr(int l,int r){
52     int x=query_ST(l,r);
53     return A[x]*(x-l+1)+fff[r]-fff[x];
54 }
55 void MD(){
56     sort(q+1,q+1+m,cmp1);
57     int l=1,r=0;
58     ll ans=0;
59     for(int i=1;i<=m;i++){
60         while(r<q[i].r)ans+=calr(l,++r);
61         while(r>q[i].r)ans-=calr(l,r--);
62         while(l>q[i].l)ans+=call(--l,r);
63         while(l<q[i].l)ans-=call(l++,r);
64         q[i].ans=ans;
65     }
66     sort(q+1,q+1+m,cmp2);
67     for(int i=1;i<=m;i++)
68         printf("%lld\n",q[i].ans);
69 }
70 int main(){
71     scanf("%d%d",&n,&m);
72     for(int i=1;i<=n;i++)
73         scanf("%lld",&A[i]);
74     PP(),build_ST();
75     int block=sqrt(n);
76     for(int i=1;i<=m;i++){
77         scanf("%d%d",&q[i].l,&q[i].r);
78         q[i].blc=(q[i].l-1)/block+1,q[i].id=i;
79     }
80     MD();
81     return 0;
82 }

时间: 2024-12-05 01:25:33

[BZOJ4540]序列的相关文章

【BZOJ4540】【HNOI2016】序列(莫队)

[BZOJ4540][HNOI2016]序列(莫队) 题面 BZOJ 洛谷 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个子序

[bzoj4540][Hnoi2016]序列——单调栈+莫队+RMQ

题目大意: 给定一个序列,每次询问一个区间[L,R]所有子区间的最小值之和. 思路: 考虑莫队如何转移,新增一个端点R,则增加的区间为[L...R-1,R],考虑这些区间新贡献的最小值. 我们把从R开始向左单调下降的序列给求出来,不难发现最小值是由区间内包含的最靠左一个在单调下降序列里的元素的值所决定的. 于是我们利用单调栈求出每一个元素前面第一个小于它的元素\(pre_i\),并求出以这个元素结尾的所有区间的最小值的和\(f_i\),不难发现\(f_i=f_{pre_i}+(i-pre_i)\

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

bzoj4540【HNOI2016】序列

4540: [Hnoi2016]序列 Time Limit: 20 Sec  Memory Limit: 512 MB Submit: 619  Solved: 302 [Submit][Status][Discuss] 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,

【BZOJ4540】【Hnoi2016】序列 线段树

Claris劲啊!CA劲啊! %%%两位线段树做法传送门在这里和这里 逆向题解时间到: 首先将询问按照终点排序,并且一边从到遍历,不妨设当前遍历到了点,对于之前的每个点,我们维护两个值和.(之后的点的两个值都先设成0) 其中表示从这个点到之间序列A的最小值,而,表示从我们遍历第一个点到当前的所有时刻下的各个历史版本的和.(当遍历的点在这个点之前等于零)(事实上.) 不(很)难发现对于每一个询问,当且仅当时,有.因为 也就是说,如果我们把询问全部离线下来,遍历的时候可以快速更新并且求和,,我们就可

【bzoj4540】[Hnoi2016]序列 单调栈+离线+扫描线+树状数组区间修改

题目描述 给出一个序列,多次询问一个区间的所有子区间最小值之和. 输入 输入文件的第一行包含两个整数n和q,分别代表序列长度和询问数.接下来一行,包含n个整数,以空格隔开,第i个整数为ai,即序列第i个元素的值.接下来q行,每行包含两个整数l和r,代表一次询问. 输出 对于每次询问,输出一行,代表询问的答案. 样例输入 5 5 5 2 4 1 3 1 5 1 3 2 4 3 5 2 5 样例输出 28 17 11 11 17 题解 单调栈+离线+扫描线+树状数组区间修改 首先把使用单调栈找出每个

如何理解“字符串是一组由16位组成的不可变的有序序列”

疑惑点: 1.16位 2.不可变 3.有序序列 解惑: 1.16位指的是:字符串每个字符所占用的空间为16bits 比特(2 bytes);这是因为JS采用的是unicode编码,每个字符需要2个字符. 2.不可变指的是: 字符串对象一旦创建出来,便不能被更改.这可能有些难理解,但事实确实如此.你可能会认为s+='1' 只是在 s 后面增加一个元素 1 而已,但事实是: 先将 s 拷贝一份,记为 temp 在 temp 末尾加上'1' 将 s 变量指向 temp,并删去原来的s 这一特性,可以从

BZOJ 3992 【SDOI2015】 序列统计

题目链接:序列统计 我来复习板子了--这道题也是我写的第一发求原根啊? 求原根方法: 从小到大依次枚举原根.设当前枚举的原根为\(x\),模数为\(p\),\(p-1\)的质因数分别为\(p_1,p_2,\dots,p_m\),则只需检验\(x^{\frac{p}{p_i}}\equiv1 \pmod{p}\)是否成立即可.如果成立则\(x\)不是原根. 然后这道题朴素\(dp\)就不讲了.设\(m\)的原根为\(g\),那么把每个数表示成\(g^k\)的形式就可以乘法变加法了,就成为了\(NT

k序列和

二分答案是参数搜索的一个改善.是这样,对于一个问题,如果它的答案具有单调性质(即如果i不可行,那么大于i的解都不可行,而小于i的解有可能可行),进而用二分的方法枚举答案,再判断答案是否可行,直到求到符合条件为止.例如:问题的答案范围是1到w之间的一个整数,求最小解,那么我们设s=1,t=w,之后mid=(s+t)整除2.然后判断当解是mid的时候这个问题能不能解决,如果能解决则和最优解比较,并且范围缩小到s到mid-1之间(因为即使这个范围没有解,那么mid是最小解):如果不能解决问题,则最小解