显然集合后相对位置不变最优。主席树上二分向左和向右的分界点即可。注意主席树的值域。我怎么天天就写点一眼题啊。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> #include<cassert> using namespace std; #define ll long long #define N 500010 #define V 1500000 char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,m,a[N],root[N],cnt; struct data{int l,r,x;ll sum; }tree[N<<5]; void ins(int &k,int l,int r,int x) { tree[++cnt]=tree[k],k=cnt;tree[k].x++;tree[k].sum+=x; if (l==r) return; int mid=l+r>>1; if (x<=mid) ins(tree[k].l,l,mid,x); else ins(tree[k].r,mid+1,r,x); } int query(int x,int y,int l,int r,int p) { if (l==r) return l; int mid=l+r>>1; if (p+tree[tree[y].l].x-tree[tree[x].l].x-1<=mid) return query(tree[x].l,tree[y].l,l,mid,p); else return query(tree[x].r,tree[y].r,mid+1,r,p+tree[tree[y].l].x-tree[tree[x].l].x); } ll sum(int x,int y,int l,int r,int p) { if (l==r) return tree[y].sum-tree[x].sum; int mid=l+r>>1; if (p<=mid) return sum(tree[x].l,tree[y].l,l,mid,p); else return sum(tree[x].r,tree[y].r,mid+1,r,p)+tree[tree[y].l].sum-tree[tree[x].l].sum; } int main() { #ifndef ONLINE_JUDGE freopen("bzoj5319.in","r",stdin); freopen("bzoj5319.out","w",stdout); const char LL[]="%I64d "; #else const char LL[]="%lld "; #endif n=read(),m=read(); for (int i=1;i<=n;i++) a[i]=read(); for (int i=1;i<=n;i++) { root[i]=root[i-1]; ins(root[i],0,V,a[i]); } while (m--) { int l=read(),r=read(),x=read(); int p=query(root[l-1],root[r],0,V,x),L=x,R=x+r-l; ll ans=(1ll*(L+p)*(p-L+1)>>1)-(1ll*(p+1+R)*(R-p)>>1); ans+=tree[root[r]].sum-tree[root[l-1]].sum-(sum(root[l-1],root[r],0,V,p)<<1); printf(LL,ans); } return 0; }