江苏OI2018 军训列队(主席树)

6894: 军训列队

时间限制: 8 Sec  内存限制: 512 MB
提交: 42  解决: 9
[提交] [状态] [讨论版] [命题人:admin]

题目描述

作为一名大学生,九条可怜在去年参加了她人生中的最后一次军训。
军训中的一个重要项目是练习列队,为了训练学生,教官给每一个学生分配了一个休息位置。每次训练开始前,所有学生都在各自的休息位置休息,但是当教官发出集合命令后,被点到的学生必须要到指定位置集合。
为了简化问题,我们把休息位置和集合位置抽象成一根数轴。一共有n个学生,第i个学生的休息位置是ai。每一次命令,教官会指定一个区间[l,r]和集合点K,所有编号在[l,r]内的学生都必须赶到集合点列队。在列队时,每一个学生需要选择[K,K+r−l]中的一个整数坐标站定且不能有任何两个学生选择的坐标相同。学生从坐标x跑到坐标y需要耗费体力|y-x|。
在一天的训练中,教官一共发布了m条命令(l,r,K),现在你需要计算对于每一条命令,在所有可能的列队方案中,消耗的体力值总和最小是多少。
以下是对题意的一些补充:
任何两条命令是无关的,即在一条集合命令结束后,所有学生都会回到自己的休息位置,然后教官才会发出下一条命令。
在集合的时候,可能有编号不在[l,r]内的学生处在区间[K,K+r−l]中,这时他会自己跑开,且跑动的距离不记在消耗的体力值总和中。

输入

第一行输入两个整数n,m。
第二行n个整数ai表示学生的休息位置。保证学生休息的位置两两不同。
接下来m行每行三个整数l,r,K表示一条命令。

输出

对于每一条命令输出一行一个整数表示最小的体力值总和。

样例输入

5 5
1 5 7 6 2
1 5 2
1 5 3
1 3 9
2 4 2
3 5 5

样例输出

5
4
17
9
3

提示

对于100%的数据,n,m≤5×105,1≤ai,K≤106
对于100%的数据,学生休息的位置两两不同。

来源/分类

江苏OI2018 

解题思路:

 把学生的位置看成数轴上的点之后,我们可以建立主席树去维护一个区间和

同时记录区间学生的个数,然设[l,r]为学生所在的区间,[L,R],集合区域构成的区间。

如果r<=R那么我们并且l-r的人数与L-R的区间长度相等,那么我们就直接平移过来就是最优的。

这时候我们直接用L-R的和减去区间l-r的和就是花费的体力值,而L-R是一个连续的区间,它的和

可以用等差数列公式求出。当L<=l,依旧可以用上述方法。 当时如果当前情况不满足上述情况怎么办。

我们就二分去查找满足情况的位置。二分的方法是,我看当前树根的左儿子区间有多少个人,即cnt记录的情况

然后我就把要查询的集合区域划分为[k,k+左儿子区间人数-1]这样我就保证了集合区域的长度与查询的区间

里的人数相等(因为只有这样才能用上述公式去解决),用同样的方法去递归解决右区间。

数组版

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<vector>
#include<cmath>
#include<algorithm>
using namespace std;
#define LL long long
#define inf 0x3f3f3f3f
#define lowb(x) x&-x
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define per(i,j,k) for(int i=j;i>=k;i--)
#define sca(x) scanf("%d",&x)
#define N 500005


inline int read()
{
    int f=1,x=0;
    char c;c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}

int T[N*25],L[N*25],R[N*25],cnt[N*25];
LL sum[N*25],a[N];
int tot=0;
void build(int &rt,int l,int r)
{
    rt=++tot;
    if(l==r)return ;
    int m=(l+r)>>1;
    build(L[rt],l,m);
    build(R[rt],m+1,r);
}

void ins(int &rt,int pre,int l,int r,LL p)
{
    rt=++tot;
    L[rt]=L[pre],R[rt]=R[pre];
    cnt[rt]=cnt[pre],sum[rt]=sum[pre];
    cnt[rt]++,sum[rt]+=p;
    if(l==r)return ;
    int m=(l+r)>>1;
    if(p<=m) ins(L[rt],L[pre],l,m,p);
    else ins(R[rt],R[pre],m+1,r,p);
}

LL query(int rl,int rr,int l,int r,int ql,int qr)
{
    if(ql>qr)return 0;
    if(r<=qr)return (LL)(ql+qr)*(qr-ql+1)/2-(sum[rr]-sum[rl]);
    if(l>=ql)return sum[rr]-sum[rl]-(LL)(ql+qr)*(qr-ql+1)/2;
    int m=(l+r)>>1;
    int del=cnt[L[rr]]-cnt[L[rl]];
    return query(L[rl],L[rr],l,m,ql,ql+del-1)+
    query(R[rl],R[rr],m+1,r,ql+del,qr);
}

int main()
{
    int n,m;
    LL Max=0;
    n=read(),m=read();
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]),Max=max(a[i],Max);
    build(T[0],1,Max);
    for(int i=1;i<=n;i++)ins(T[i],T[i-1],1,Max,a[i]);
    while(m--)
    {
        int l,r,k;
        l=read(),r=read(),k=read();
        printf("%lld\n",query(T[l-1],T[r],1,Max,k,k+r-l));
    }
}

结构体版:

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<vector>
#include<cmath>
#include<algorithm>
using namespace std;
#define LL long long
#define inf 0x3f3f3f3f
#define lowb(x) x&-x
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define per(i,j,k) for(int i=j;i>=k;i--)
#define sca(x) scanf("%d",&x)
#define N 500005
LL M=0;
int n,m,tot=0;
struct node
{
    int ls,rs,cnt;
    LL sum;
}t[N*25];
int T[N*25];
LL a[N*25];
void build(int &rt,int l,int r)
{
    rt=++tot;
    t[rt].cnt=0,t[rt].sum=0;
    if(l==r)return ;
    int m=(l+r)>>1;
    build(t[rt].ls,l,m);
    build(t[rt].rs,m+1,r);
}
 
void ins(int &now,int pre,int l,int r,LL p)
{
    now=++tot;
    t[now]=t[pre];
    t[now].sum+=p,t[now].cnt++;
    if(l==r)return ;
    int m=(l+r)>>1;
    if(p<=m)ins(t[now].ls,t[pre].ls,l,m,p);
    else ins(t[now].rs,t[pre].rs,m+1,r,p);
}
 
LL query(int r1,int r2,int l,int r,int L,int R)
{
    if(L>R)return 0;
    if(r<=R)return  (LL)(L+R)*(R-L+1)/2-(t[r2].sum-t[r1].sum);
    if(l>=L)return  (t[r2].sum-t[r1].sum)-(LL)(LL)(L+R)*(R-L+1)/2;
    int m=(l+r)>>1;
    int del=t[t[r2].ls].cnt-t[t[r1].ls].cnt;
    return query(t[r1].ls,t[r2].ls,l,m,L,L+del-1)+
    query(t[r1].rs,t[r2].rs,m+1,r,L+del,R);
}
 
int read()
{
    int f=1,x=0;
    char c;c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
 
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++){scanf("%lld",&a[i]);M=max(M,a[i]);}
    build(T[0],1,M);
    for(int i=1;i<=n;i++)ins(T[i],T[i-1],1,M,a[i]);
    for(int i=1;i<=m;i++)
    {
        int l,r,k;
        l=read();r=read();k=read();
        printf("%lld\n",query(T[l-1],T[r],1,M,k,k+r-l));
    }
}

参考博客:https://blog.csdn.net/qq_30974369/article/details/80517196

猜你喜欢

转载自blog.csdn.net/weixin_40894017/article/details/81428547