P4556 题解
P4556 题解
这道题一开始读错题了,导致思路走偏。
考虑到编号是一段区间,我们立马就可以想到主席树来做,不难发现一定是左边的人往右边跑,右边的人往左边跑,所以我们相当于是要在线段树上去二分一个分界点,这是因为我们一定可以找到一个最优方案,这些编号先对位置没有变化。
具体来说,我们建立一棵主席树,然后考虑,向右边跑的和向左边跑的两边的贡献,我们在主席树上二分,当左右两边都往同一个方向跑的时候,我们计算答案,否则我们继续往下递归。如果当前区间里面已经没有数了,我们也停止递归。
然后我们来考虑一下如何计算答案,不难发现,答案的形式实际上是区间和和等差数列,所以我们直接硬做就可以了。
时间复杂度大概是二分一个分界点的复杂度,可以接受。
代码:
#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define int long long
#define uint unsigned int
#define ull unsigned long long
#define N 20000000
#define M number
using namespace std;
const int INF=0x3f3f3f3f;
const int len=1000000;
template<typename T> inline void read(T &x) {
x=0; int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
x*=f;
}
struct Node{
int ls,rs,Size,sum;
inline Node(){}
inline Node(int ls,int rs,int Size,int sum) : ls(ls),rs(rs),Size(Size),sum(sum) {}
}p[N];
int tot,root[N],n,m,a[N];
#define ls(k) p[k].ls
#define rs(k) p[k].rs
struct SegmentTree{
inline void PushUp(int k){
p[k].sum=p[ls(k)].sum+p[rs(k)].sum;
p[k].Size=p[ls(k)].Size+p[rs(k)].Size;
}
inline void Insert(int &k,int last,int l,int r,int w,int val){
k=++tot;p[k]=p[last];
if(l==r){p[k].sum+=w;p[k].Size++;return;}int mid=(l+r)>>1;
if(w<=mid) Insert(ls(k),ls(last),l,mid,w,val);
else Insert(rs(k),rs(last),mid+1,r,w,val);
PushUp(k);
}
inline int Solve(int lk,int rk,int k,int l,int r,int rank){
int nowsize=p[rk].Size-p[lk].Size,nowsum=p[rk].sum-p[lk].sum;
// printf("lk=%d rk=%d k=%d l=%d r=%d rank=%d\n",lk,rk,k,l,r,rank);
if(nowsize==0||nowsum==0) return 0;int mid=(l+r)>>1;
if(l<=k+rank-1&&r<=k+rank+nowsize-2) return (2*k+2*rank+nowsize-3)*(nowsize)/2-nowsum;
else if(l>=k+rank-1&&r>=k+rank+nowsize-2) return nowsum-(2*k+2*rank+nowsize-3)*(nowsize)/2;
else return Solve(ls(lk),ls(rk),k,l,mid,rank)+Solve(rs(lk),rs(rk),k,mid+1,r,rank+p[ls(rk)].Size-p[ls(lk)].Size);
}
}tr;
signed main(){
// freopen("my.in","r",stdin);
// freopen("my.out","w",stdout);
read(n);read(m);
for(int i=1;i<=n;i++){
read(a[i]);tr.Insert(root[i],root[i-1],1,len,a[i],1);
}
for(int i=1;i<=m;i++){
int l,r,k;read(l);read(r);read(k);
printf("%lld\n",tr.Solve(root[l-1],root[r],k,1,len,1));
}
return 0;
}