[HNOI2016] 序列
一看可以离线果断上扫描线,然后再开一颗线段树,叶子 i 记录 右端点<=目前扫描线右端点,左端点在i的所有区间的最小值之和。
然后直接扫描就行了,需要开个单调栈维护更新线段树的过程。复杂度玄学,全看数据。
#include<bits/stdc++.h> #define ll long long using namespace std; #define pb push_back #define lc (o<<1) #define mid (l+r>>1) #define rc ((o<<1)|1) const int N=1e5+5; inline int read(){ int x=0,f=1; char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; return x*f; } void W(ll x){ if(x>=10) W(x/10); putchar(x%10+'0');} struct node{ int l,num;}; vector<node> ask[N]; int s[N],tp,n,a[N],le,ri,w,Q,len[N*4]; ll tag[N*4],sum[N*4],ans[N],*now; void build(int o,int l,int r){ len[o]=r-l+1; if(l==r) return; build(lc,l,mid),build(rc,mid+1,r); } inline void Get(int x,ll W){ tag[x]+=W,sum[x]+=W*(ll)len[x]; } inline void pd(int o){ Get(lc,tag[o]),Get(rc,tag[o]),tag[o]=0; } void update(int o,int l,int r){ if(l>=le&&r<=ri){ Get(o,w); return;} pd(o); if(le<=mid) update(lc,l,mid); if(ri>mid) update(rc,mid+1,r); sum[o]=sum[lc]+sum[rc]; } void query(int o,int l,int r){ if(l>=le&&r<=ri){ *now+=sum[o]; return;} pd(o); if(le<=mid) query(lc,l,mid); if(ri>mid) query(rc,mid+1,r); } inline void Geng(int p){ while(tp&&a[p]<=a[s[tp]]) tp--; s[++tp]=p; for(int i=1;i<=tp;i++) le=s[i-1]+1,ri=s[i],w=a[s[i]],update(1,1,n); } inline void solve(){ for(int i=1;i<=n;i++){ Geng(i); for(node u:ask[i]) now=ans+u.num,le=u.l,ri=i,query(1,1,n); } } int main(){ n=read(),Q=read(),a[0]=-(1<<30); for(int i=1;i<=n;i++) a[i]=read(); for(int i=1;i<=Q;i++) le=read(),ri=read(),ask[ri].pb((node){le,i}); build(1,1,n),solve(); for(int i=1;i<=Q;i++){ if(ans[i]<0) ans[i]=-ans[i],putchar('-'); W(ans[i]),puts(""); } return 0; }
我爱学习,学习使我快乐