bzoj4540
给定长度为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],这6个子序列的最小值之和为5+2+4+2+2+2=17。
输入文件的第一行包含两个整数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
1 ≤N,Q ≤ 100000,|Ai| ≤ 10^9
这里提供一种 nn√ 的做法。
我们考虑使用莫队算法,当左右端点移动时,只会增加或删除以某个元素开头或结尾的子串。这里我们考虑右端点右移至 r 。那么新增的子串为 s[i...r](l<=i<=r) 。我们考虑 l 至 r 中的最小值 p ,假设他的位置是 k ,则它对答案的贡献为 (k−l+1)∗p 。我们记 ls[i] 表示 i 的左边第一个比 i 小的元素的位置。然后我们发现第 r 个元素对答案的贡献是 (i−ls[i])∗a[i],i 至 ls[i]+1 中的元素对答案没有贡献。第 ls[i] 个元素对答案的贡献是 (ls[i]−ls[ls[i]])∗a[ls[i]] ,依次列推直到 p 。这样我们可以预处理一个类似前缀和的数组,然后通过rmq可以求出 p ,这样就可以 O(1) 实现转移了。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
using namespace std;
const int N=100010;
int stk[N],a[N],rmq[N][20],r[N],l[N],Log[N],top,S,n,m,x,y;
long long ans[N],dl[N],dr[N],now;
struct query{
int l,r,id;
friend bool operator <(const query &a,const query &b){
if(a.l/S==b.l/S) return a.r<b.r;
return a.l/S<b.l/S;
}
}q[N];
inline void Read(int &x){
static char c;
int f=1;
for(c=getchar();c<'0'||c>'9';c=getchar()) if(c=='-') f=-1;
for(x=0;c>='0'&&c<='9';c=getchar()) x=x*10+c-'0';
x=x*f;
}
int query(int l,int r){
int tmp=Log[r-l+1];
if(a[rmq[l][tmp]]<a[rmq[r-(1<<tmp)+1][tmp]])
return rmq[l][tmp];
else return rmq[r-(1<<tmp)+1][tmp];
}
void changel(int l,int r,long long k){
int tmp=query(l,r);
now+=k*a[tmp]*(r-tmp+1);
now+=k*(dr[l]-dr[tmp]);
}
void changer(int l,int r,long long k){
int tmp=query(l,r);
now+=k*a[tmp]*(tmp-l+1);
now+=k*(dl[r]-dl[tmp]);
}
void pretreat(){
for(int i=1;i<=n;i++) Log[i]=(int)log2(i);
for(int i=1;i<=n;i++){
while(top&&a[stk[top]]>a[i]) r[stk[top--]]=i;
l[i]=a[stk[top]]==a[i]?l[stk[top]]:stk[top];
stk[++top]=i;
}
while(top) r[stk[top--]]=n+1;
for(int i=1;i<=n;i++) dl[i]=dl[l[i]]+1ll*(i-l[i])*a[i];
for(int i=n;i;i--) dr[i]=dr[r[i]]+1ll*(r[i]-i)*a[i];
for(int i=1;i<=n;i++) rmq[i][0]=i;
for(int i=1;(1<<i)<=n;i++)
for(int j=1;j<=n-(1<<i)+1;j++)
if(a[rmq[j][i-1]]<a[rmq[j+(1<<(i-1))][i-1]])
rmq[j][i]=rmq[j][i-1];
else rmq[j][i]=rmq[j+(1<<(i-1))][i-1];
}
int main(){
Read(n); Read(m);
S=(int)sqrt(n);
for(int i=1;i<=n;i++) Read(a[i]);
pretreat();
for(int i=1;i<=m;i++){
Read(q[i].l); Read(q[i].r);
q[i].id=i;
}
sort(q+1,q+1+m);
x=y=1; now=a[1];
for(int i=1;i<=m;i++){
if(q[i].r>y) while(y!=q[i].r) y++,changer(x,y,1);
if(q[i].l>x) while(x!=q[i].l) changel(x,y,-1),x++;
if(q[i].l<x) while(x!=q[i].l) x--,changel(x,y,1);
if(q[i].r<y) while(y!=q[i].r) changer(x,y,-1),y--;
ans[q[i].id]=now;
}
for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
return 0;
}