• BZOJ5319/LOJ2551「JSOI2018」列队


    问题描述

    作为一名大学生,九条可怜在去年参加了她人生中的最后一次军训。

    军训中的一个重要项目是练习列队,为了训练学生,教官给每一个学生分配了一个休息位置。每次训练开始前,所有学生都在各自的休息位置休息,但是当教官发出集合命令后,被点到的学生必须要到指定位置集合。

    为了简化问题,我们把休息位置和集合位置抽象成一根数轴。一共有 (n) 个学生,第 (i) 个学生的休息位置是 (a_i)。每一次命令,教官会指定一个区间 ([l,r]) 和集合点 (K) ,所有编号在 ([l,r]) 内的学生都必须赶到集合点列队。在列队时,每一个学生需要选择 ([K,K+r-l]) 中的一个整数坐标站定且不能有任何两个学生选择的坐标相同。学生从坐标 (x) 跑到坐标 (y) 需要耗费体力 (|y-x|)

    在一天的训练中,教官一共发布了 (m) 条命令 ((l,r,K)) ,现在你需要计算对于每一条命令,在所有可能的列队方案中,消耗的体力值总和最小是多少。

    以下是对题意的一些补充:

    • 任何两条命令是无关的,即在一条集合命令结束后,所有学生都会回到自己的休息位置,然后教官才会发出下一条命令。
    • 在集合的时候,可能有编号不在 ([l,r]) 内的学生处在区间 ([K,K+r-l]) 中,这时他会自己跑开,且跑动的距离不记在消耗的体力值总和中。

    题解

    显然,不改变原相对顺序更优。

    因此答案为 (sumlimits_{i=l}^r{a_i+rk_i-k-1})

    把这个式子拆掉绝对值,就变为了向左跑和向右跑的两种情况。

    在可持久化权值线段树上记录 (sum a_i) 即可。


    (mathrm{Code})

    从今天起更改码风。

    #include<bits/stdc++.h>
    using namespace std;
    
    typedef long long LL;
    
    const int maxn = 500007;
    const int maxs = 11000007;
    const int s = 1000000;
    
    int n, T;
    int rt[maxn];
    int ls[maxs], rs[maxs], cnt;
    int size[maxs];
    
    LL sum[maxs];
    
    void Insert(int &x ,int l, int r, int val) {
    	ls[++cnt] = ls[x], rs[cnt] = rs[x], size[cnt] = size[x] + 1, sum[cnt] = sum[x] + (LL)val;
    	x = cnt;
    	if(l == r) return ;
    	int mid = (l + r) >> 1;
    	if(val <= mid) Insert(ls[x], l, mid, val);
    	else Insert(rs[x], mid + 1, r, val);
    }
    
    int k;
    
    LL Query(int rt1, int rt2, int l, int r, int st) {
    	if(size[rt1] - size[rt2] == 0) return 0ll;
    	LL sz = size[rt1] - size[rt2], sigma = sum[rt1] - sum[rt2];
    	if(l >= k + st) return sigma - sz * (2 * st + 2 * k + sz - 1) / 2;
    	if(r <= k + st + sz - 1) return sz * (2 * st + 2 * k + sz - 1) / 2 - sigma;
    	int mid = (l+r) >> 1;
    	return Query(ls[rt1], ls[rt2], l, mid, st) + Query(rs[rt1], rs[rt2], mid + 1, r, st + size[ls[rt1]] - size[ls[rt2]]);
    }
    
    void Init(void) {
    	scanf("%d%d", &n, &T);
    	for(int i = 1, x; i <= n; i++) {
    		scanf("%d", &x);
    		rt[i] = rt[i-1];
    		Insert(rt[i], 1, s, x);
    	}
    }
    
    void Work(void) {
    	while(T--) {
    		int l, r;
    		scanf("%d%d%d", &l, &r, &k);
    		printf("%lld
    ",Query(rt[r], rt[l-1], 1, s, 0));
    	}
    }
    
    int main() {
    	Init();
    	Work();
    	return 0;
    }
    
  • 相关阅读:
    Nginx的访问控制
    远程登录
    Linux的网络命令
    laravel-collect
    laravel-model
    laravel-Macroable
    laravel-容器
    机器学习-Logisitic回归
    机器学习-多变量线性回归
    算法笔记-分支界限法
  • 原文地址:https://www.cnblogs.com/liubainian/p/12350173.html
Copyright © 2020-2023  润新知