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
对于每次询问,输出一行,代表询问的答案。
题解:
参照于网上某位大佬。。。-----http://www.cnblogs.com/ccz181078/p/6227639.html
对于每个a[i],若其在[l,r]有贡献,满足l<=i,r>=i,,a[l...i]>=a[i],a[i...r]>=a[i],然后我们可以预处理出每个a[i]统辖的l,r的位置。然后对于每个询问L,R,包含子序列l,r满足L<=l<=r<=R,转化一下,就是在一个平面中求L<=x<=R,L<=y<=R这个矩形面积(不满足前一个式子的可以不加)。这个时候就可以用二维树状数组或者线段树O(n(logn)^2)乱搞了。但时间还是不优,考虑优化。把询问离线,按照L从大到小排序。令与y轴平行的扫描线沿x轴负方向扫描,把符合当前条件的修改添加。
然后就是喜闻乐见的差分了=-=。(调了4个小时。。。绝望TAT)对于每个三元组(l,op,r)l,r为op极大区域内满足op最小的左右端点。(比如2、4、5、1,对于op==2,满足l==2,r==3)那么考虑对每次询问的贡献,对于某个三元组,在扫描线扫的时候,我们把贡献分成进入\离开该三元组,将贡献进行差分。(此处不太好理解。)当我们进入该区间时,我们直接先将其在平面上对应的矩形面积拓展到坐标轴上,离开时,再将之前多覆盖的减去。当计算答案时(还是2、4、5、1,如询问L=3,R=4时,我们显然看到op==4的左端点小于3),我们需要再次差分,因为我添加时是把op~r添加的,对于答案的贡献就是min(op-i[当前询问],op-L[op])*(r[op]-op)*a[op],又一开始时设置了将面积添加改变为两部分,min(op-L[当前询问],op-l[op])直接就可以改为op-L[当前询问],分成两个树状数组,对于第一个数组,记录需要减去的a[op],第二个直接记录全部贡献。最后统计时再给第一个乘上L[当前询问]。对于每个树状数组,其实都是假二维,因为我们一个点是影响一个矩形的取值,所以在y轴方向上直接乘上x的大小,时间就优化了下来。
这个可能解释得不大清楚。。。(我真的尽力了。。。)如果您有疑问(且愿意)可以参照代码理解。。。(毕竟我这么弱。。。。)
#include<cstdio> #include<algorithm> typedef long long ll; const int N=100100; int n,q,a[N]; int que[N],top,l[N],r[N]; ll ans[N]; int read(){ int x=0,f=1; char ch=getchar(); while(ch<48||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>47&&ch<='9')x=x*10+ch-48,ch=getchar(); return x*f; } ll c1[2][N],c2[2][N]; void add(ll*a1,ll*a2,int x,ll w){ if(!x)return; for(int i=x;i;i-=i&-i)a1[i]+=w; w*=x; for(int i=x;i<=n;i+=i&-i)a2[i]+=w; } ll sum(ll*a1,ll*a2,int x){ if(!x)return 0; ll s=0; for(int i=x;i<=n;i+=i&-i)s+=a1[i]; s*=x; for(int i=x-1;i;i-=i&-i)s+=a2[i]; return s; } struct Query{ int l,r,id; void get_ans(){ ans[id]=sum(c1[0],c1[1],r)*l+sum(c2[0],c2[1],r); } friend bool operator<(Query a,Query b){return a.l>b.l;} }query[N]; struct P{ int l,r1,r2; ll k,b; void push(){ add(c1[0],c1[1],r2,k),add(c1[0],c1[1],r1-1,-k); add(c2[0],c2[1],r2,b),add(c2[0],c2[1],r1-1,-b); } friend bool operator<(P a,P b){return a.l>b.l;} }e[N*2]; int ep=0; int main(){ // freopen("sequence2.in","r",stdin); // freopen("sequence.out","w",stdout); n=read();q=read(); for(int i=1;i<=n;++i)a[i]=read(); for(int i=0;i<q;++i) query[i].l=read(),query[i].r=read(),query[i].id=i; std::sort(query,query+q); for(int i=1;i<=n;++i){ while(top&&a[que[top]]>a[i])r[que[top--]]=i-1; que[++top]=i; } while(top)r[que[top--]]=n; for(int i=n;i;--i){ while(top&&a[que[top]]>=a[i])l[que[top--]]=i+1; que[++top]=i; } while(top)l[que[top--]]=1; for(int i=1;i<=n;++i){ e[ep++]=(P){i,i,r[i],-a[i],ll(i+1)*a[i]}; e[ep++]=(P){l[i],i,r[i],a[i],ll(-l[i])*a[i]}; } std::sort(e,e+ep); for(int i=0,p=0;i<q;++i){ while(p<ep&&e[p].l>=query[i].l)e[p++].push(); query[i].get_ans(); } for(int i=0;i<q;++i)printf("%lld\n",ans[i]); return 0; }