P4559 [JSOI2018] 列队

题意

很多个学生,每个学生有一个初始位置,每次询问求编号为 \([l,r]\) 的学生移动到位置 \([K,K+r-l]\),花费为移动的距离。所有学生的目标位置不能重复,起始位置各不相同,询问之间互不影响,求花费总和的最小值。

思路

显然,对于一次询问 \((l,r,k)\),设 \(L=k\)\(R=k+r-l\)。为了使方案最优,所有满足条件的学生在排好队后的相对位置是不变的。位置在 \([1,L)\) 的学生会占据前面的位置,而这些位置的编号是比祂们的位置编号大的,直接统计一下个数和位置编号和即可,位置在 \((R,+\infty)\) 的学生同理。这个东西是二维的,可以用主席树维护,把学生的编号当作版本,位置当作下标即可。
考虑处理位置在 \([L,R]\) 的学生。祂们难处理是因为无法判断祂们的位置和目标位置的大小关系。观察发现,\([L,R]\) 的学生中位置在前面的一部分目标位置在后面,其祂学生目标位置在前面。
考虑二分第一个目标位置在祂前面的点,设位置在 \([1,L)\) 的学生数量为 \(u\),我们找的就是区间内第一个(位置-在 \([L,R]\) 中的排名+1 \(\ge\)+\(L\)+\(u\))的点,祂就是第一个目标点再前面的学生。然后直接按照 \([1,L)\)\((R,+\infty)\) 的学生的的算法计算即可。

代码

可能有点卡常,要写主席树上二分,不能二分套主席树。

/*
Luogu P4559 [JSOI2018] 列队
2026-04-08
*/
#include<bits/stdc++.h>
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T&x){
        x=0;char c=getchar();bool f=0;
        while(!isdigit(c)) c=='-'?f=1:0,c=getchar();
        while(isdigit(c)) x=x*10+c-'0',c=getchar();
        f?x=-x:0;
    }
    template<typename T>
    inline void write(T x){
        if(x==0){putchar('0');return ;}
        x<0?x=-x,putchar('-'):0;short st[50],top=0;
        while(x) st[++top]=x%10,x/=10;
        while(top) putchar(st[top--]+'0');
    }
    inline void read(char&c){c=getchar();while(isspace(c)) c=getchar();}
    inline void write(char c){putchar(c);}
    inline void read(string&s){s.clear();char c;read(c);while(!isspace(c)&&~c) s+=c,c=getchar();}
    inline void write(string s){for(int i=0,len=s.size();i<len;i++) putchar(s[i]);}
    template<typename T>inline void write(T*x){while(*x) putchar(*(x++));}
    template<typename T,typename...T2> inline void read(T&x,T2&...y){read(x),read(y...);}
    template<typename T,typename...T2> inline void write(const T x,const T2...y){write(x),putchar(' '),write(y...),sizeof...(y)==1?putchar('\n'):0;}
}using namespace IO;
#define LL long long
const int maxn=500010,maxw=1500010;
int n,m,maxx=1500000,a[maxn];
class Segment_Tree{
private:
    struct node{int ch[2],sz;LL sum;}t[maxw*40];
    int rt[maxn],cnt;
    void insert(int&u,int uu,int l,int r,int d,int z){
        u=++cnt;
        if(l==r){t[u].sz=1,t[u].sum=z;return ;}
        int mid=l+r>>1;
        t[u]=t[uu];
        if(mid>=d) insert(t[u].ch[0],t[uu].ch[0],l,mid,d,z);
        else insert(t[u].ch[1],t[uu].ch[1],mid+1,r,d,z);
        t[u].sz=t[t[u].ch[0]].sz+t[t[u].ch[1]].sz;
        t[u].sum=t[t[u].ch[0]].sum+t[t[u].ch[1]].sum;
    }
    pair<int,LL> add(pair<int,LL>a,pair<int,LL>b){return{a.first+b.first,a.second+b.second};}
    pair<int,LL> query(int u,int uu,int l,int r,int ll,int rr){
        if(l>rr||r<ll) return {0,0};
        if(ll<=l&&r<=rr) return {t[u].sz-t[uu].sz,t[u].sum-t[uu].sum};
        int mid=l+r>>1;
        return add(query(t[u].ch[0],t[uu].ch[0],l,mid,ll,rr),query(t[u].ch[1],t[uu].ch[1],mid+1,r,ll,rr));
    }
    int find(int u,int uu,int l,int r,int L,int sz){
        if(l==r) return l;
        int mid=l+r>>1;
        int sz_L=t[t[u].ch[0]].sz-t[t[uu].ch[0]].sz;
        if(mid>=sz+sz_L+L-1) return find(t[u].ch[0],t[uu].ch[0],l,mid,L,sz);
        return find(t[u].ch[1],t[uu].ch[1],mid+1,r,L,sz+sz_L);
    }
public:
    void insert(int id,int d,int z){insert(rt[id],rt[id-1],1,maxx,d,z);}
    pair<int,LL> query(int idd,int id,int l,int r){return query(rt[id],rt[idd-1],1,maxx,l,r);}
    int find(int idd,int id,int add){return find(rt[id],rt[idd-1],1,maxx,add,0);}
}t;
LL calc(int l,int r,int L,int R,int L_sz){
    int ans=t.find(l,r,L);
    pair<int,LL>ls1=t.query(l,r,L,ans-1),ls2=t.query(l,r,ans,R);
    int sz1=ls1.first,sz2=ls2.first;
    LL re,sum1=1ll*(L+L_sz+L+L_sz+sz1-1)*sz1/2,sum2=1ll*(L+L_sz+sz1+L+L_sz+sz1+sz2-1)*sz2/2;
    return sum1-ls1.second+ls2.second-sum2;
}
signed main(){
    read(n,m);
    for(int i=1;i<=n;i++) read(a[i]);
    for(int i=1;i<=n;i++) t.insert(i,a[i],a[i]);
    for(int i=1;i<=m;i++){
        int l,r,K;read(l,r,K);
        int L=K,R=K+r-l;
        LL ans=0;
        pair<int,LL>ls1=t.query(l,r,1,L-1),ls2=t.query(l,r,R+1,maxx);
        int L_sz=ls1.first,R_sz=ls2.first;
        ans+=1ll*(L+L+L_sz-1)*L_sz/2-ls1.second;
        ans+=ls2.second-1ll*(R-R_sz+1+R)*R_sz/2;
        ans+=calc(l,r,L,R,L_sz);
        write(ans);write("\n");
    }
    return 0;
}
posted @ 2026-04-08 21:46  Link-Cut_Trees  阅读(16)  评论(0)    收藏  举报