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],这6个子序列的最小值之和为5+2+4+2+2+2=17。
Input
输入文件的第一行包含两个整数n和q,分别代表序列长度和询问数。
接下来一行,包含n个整数,以空格隔开,第i个整数为ai,即序列第i个元素的值。
接下来q行,每行包含两个整数l和r,代表一次询问。
Output
对于每次询问,输出一行,代表询问的答案。
Sample Input
5 5
5 2 4 1 3
1 5
1 3
2 4
3 5
2 5
5 2 4 1 3
1 5
1 3
2 4
3 5
2 5
Sample Output
28
17
11
11
17
17
11
11
17
HINT
1 ≤N,Q ≤ 100000,|Ai| ≤ 10^9
题解Here!
字符串做得要吐了,于是又回来搞大力数据结构。。。
然后发现还是数据结构好写555。。。
这个题反正$RMQ$跑不了。
然后就是怎么求子区间的和了。
没有强制在线,我们可以拿出区间神器——莫队!
莫队的难点就在于怎么由区间$[l,r]$推出$[l,r+1]$。
考虑他们之间的增加量就是新增的区间$[l,r+1],[l+1,r+1],[l+2,r+1],...,[r+1,r+1]$的最小值之和。
一共$r-l+2$个区间。
如果我们知道区间$[l,r+1]$的最小值位置为$pos$,那么左端点在区间$[l,pos]$之前的区间答案都是$val[pos]$。
对答案的贡献就是$val[pos]\times (pos-l+1)$。
至于$pos$怎么求,当然用$O(1)$的$ST$表啦。。。
只不过$ST$表维护的是最小值的位置。
再考虑剩下的左端点在$[pos+1,r+1]$之间的区间。
这里引入一个类似前缀和的东西。
我们用$front[i]$表示$i$左边第一个比$val[i]$小的数的位置,$next[i]$表示右边。
这个过程显然可以用单调栈$O(n)$预处理。
然后开2个数组$maxl,maxr$。
$maxl[i]$表示$[1,i]$的贡献,那么就有:$maxl[i]=maxl[front[i]]+(i-front[i])*val[i]$
$maxr$同理。
这样,我们就可以在$O(1)$时间内转移了。
那么,从$[l,r]$到$[l,r-1]$呢?
我们只要减去$[l,r-1]->[l,r]$的贡献即可。
从$[l,r]$到$[l+1,r]$与从$[l,r]$到$[l-1,r]$就利用对称性求解即可。
然后我们就可以愉快地跑莫队了。
附代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cmath> #include<cstring> #define MAXN 100010 using namespace std; int n,m; int val[MAXN],f[MAXN][30],front[MAXN],next[MAXN]; long long ans[MAXN],maxl[MAXN],maxr[MAXN]; struct node{ int l,r,id; }que[MAXN]; struct Stack{ int value[MAXN],numtop; Stack(){ memset(value,0,sizeof(value)); numtop=0; } inline void push(int x){value[++numtop]=x;} inline void pop(){value[numtop--]=0;} inline int top(){return value[numtop];} inline int empty(){return (numtop==0?1:0);} }stack; inline int read(){ int date=0,w=1;char c=0; while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} return date*w; } bool cmp1(const node &x,const node &y){ return x.l<y.l; } bool cmp2(const node &x,const node &y){ if(x.r==y.r)return x.l<y.l; return x.r<y.r; } void step(){ for(int i=1;(1<<i)<=n;i++) for(int j=1;j+(1<<i)-1<=n;j++){ if(val[f[j][i-1]]>val[f[j+(1<<(i-1))][i-1]])f[j][i]=f[j+(1<<(i-1))][i-1]; else f[j][i]=f[j][i-1]; } } int query(int l,int r){ int k=0; while((1<<(k+1))<=r-l+1)k++; if(val[f[l][k]]>val[f[r-(1<<k)+1][k]])return f[r-(1<<k)+1][k]; return f[l][k]; } void work(){ int left=1,right=0; long long s=0; for(int i=1;i<=m;i++){ while(right<que[i].r) { right++; int pos=query(left,right); s+=(long long)val[pos]*(pos-left+1)+maxl[right]-maxl[pos]; } while(que[i].l>left) { int pos=query(left,right); s-=(long long)val[pos]*(right-pos+1)+maxr[left]-maxr[pos]; left++; } while(right>que[i].r) { int pos=query(left,right); s-=(long long)val[pos]*(pos-left+1)+maxl[right]-maxl[pos]; right--; } while(que[i].l<left) { left--; int pos=query(left,right); s+=(long long)val[pos]*(right-pos+1)+maxr[left]-maxr[pos]; } ans[que[i].id]=s; } for(int i=1;i<=m;i++)printf("%lld\n",ans[i]); } void init(){ int nowi=1,nowj=0,d,x; n=read();m=read(); for(int i=1;i<=n;i++){ val[i]=read(); f[i][0]=i; } step(); for(int i=1;i<=m;i++){ que[i].l=read();que[i].r=read(); que[i].id=i; } x=sqrt(n); sort(que+1,que+m+1,cmp1); while(nowi<=m){ nowj++; d=nowi; while(que[nowi].l<nowj*x&&nowi<=m)nowi++; sort(que+d,que+nowi,cmp2); if(nowj==x){ sort(que+d,que+m+1,cmp2); break; } } for(int i=1;i<=n;i++){ for(;!stack.empty()&&val[i]<val[stack.top()];stack.pop())next[stack.top()]=i; front[i]=stack.top(); stack.push(i); } for(;!stack.empty();stack.pop())next[stack.top()]=n+1; for(int i=1;i<=n;i++)maxl[i]=maxl[front[i]]+(long long)val[i]*(i-front[i]); for(int i=n;i>=1;i--)maxr[i]=maxr[next[i]]+(long long)val[i]*(next[i]-i); } int main(){ init(); work(); return 0; }