• [JSOI2018]列队


    Description:

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

    Hint:

    (n,m le 10^5,a_i,k le 10^6)

    Solution:

    都是列队,都是毒瘤题,懂的都懂

    好吧其实也不算毒瘤

    思路很容易想到,就是集合的相对顺序与休息时的相对顺序一样时体力值和取到最小

    关键是怎么统计这个最小值

    可以发现所有人可以分成向左走和向右走

    向左走的贡献: (sum k+rk_i-1-a_i)

    向右走的贡献: (sum a_i-k+rk_i-1)

    (sum a_i) 是定值 (sum k+rk_i-1) 是个等差数列,直接算就行

    如何判断向左向右呢? 魔改版主席树一直递归左右区间求解,具体看代码

    #include <map>
    #include <set>
    #include <stack>
    #include <cmath>
    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    const int bd=1e6+5,mxn=5e5+5,mxm=2e7+5;
    int n,m,cnt,a[mxn],hd[mxn];
    int rt[mxm],ls[mxm],rs[mxm],sz[mxm];
    ll sum[mxm];
    
    inline int read() {
    	char c=getchar(); int x=0,f=1;
    	while(c>'9'||c<'0') {if(c=='-') f=-1;c=getchar();}
    	while(c<='9'&&c>='0') {x=(x<<3)+(x<<1)+(c&15);c=getchar();}
    	return x*f;
    }
    inline void chkmax(int &x,int y) {if(x<y) x=y;}
    inline void chkmin(int &x,int y) {if(x>y) x=y;}
    
    struct ed {
    	int to,nxt;
    }t[mxn<<1];
    
    inline void add(int u,int v) {
    	t[++cnt]=(ed) {v,hd[u]}; hd[u]=cnt;
    }
    
    void update(int las,int &p,int l,int r,int val)
    {
    	if(!p) p=++cnt; sz[p]=sz[las]+1; sum[p]=sum[las]+val;
    	if(l==r) return ; int mid=(l+r)>>1;
    	if(val<=mid) update(ls[las],ls[p],l,mid,val),rs[p]=rs[las];
    	else update(rs[las],rs[p],mid+1,r,val),ls[p]=ls[las];
    }
    
    ll query(int las,int p,int l,int r,int s,int k) 
    {
    	if(!p) return 0;
    	ll num=sz[p]-sz[las],val=sum[p]-sum[las];
    	if(l>=k+s) return val-(2*k+2*s+num-1)*num/2;
    	if(r<=k+s+num-1) return (2*k+2*s+num-1)*num/2-val;
    	int mid=(l+r)>>1; num=sz[ls[p]]-sz[ls[las]];
    	return query(ls[las],ls[p],l,mid,s,k)+query(rs[las],rs[p],mid+1,r,s+num,k);
    }
    
    int main()
    {
    	n=read(); m=read(); int l,r,k;
    	for(int i=1;i<=n;++i) a[i]=read(),update(rt[i-1],rt[i],1,bd,a[i]);
    	for(int i=1;i<=m;++i) {
    		l=read(); r=read(); k=read(); 
    		printf("%lld
    ",query(rt[l-1],rt[r],1,bd,0,k));
    	}
        return 0;
    }
    
    
  • 相关阅读:
    Counting Sort and Radix Sort
    Naïve Bayes
    windows c/c++ 代码运行时间,毫秒级
    数学基础:四、树的应用1(利用树结构存储字典表)(待优化)
    数学基础:三、动态规划2(求解凑齐钱的最小张数)
    数学基础:三、动态规划1(求解编辑距离)
    数学基础:二、组合算法(递归)
    数学基础:一、排列算法(递归)
    idea注释模版设置
    幂等性的实现
  • 原文地址:https://www.cnblogs.com/list1/p/10555747.html
Copyright © 2020-2023  润新知