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%的数据,学生休息的位置两两不同。
来源/分类
解题思路:
把学生的位置看成数轴上的点之后,我们可以建立主席树去维护一个区间和
同时记录区间学生的个数,然设[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