P4559 [JSOI2018]列队

\(\color{#0066ff}{ 题目描述 }\)

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

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

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

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

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

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

\(\color{#0066ff}{输入格式}\)

第一行输入两个整数 \(n,m\)。

第二行 \(n\) 个整数 \(a_i\) 表示学生的休息位置。保证学生休息的位置两两不同。

接下来 \(m\) 行每行三个整数 \(l,r,K\) 表示一条命令。

\(\color{#0066ff}{输出格式}\)

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

\(\color{#0066ff}{输入样例}\)

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

\(\color{#0066ff}{输出样例}\)

5
4
17
9
3

\(\color{#0066ff}{数据范围与提示}\)

在第一条命令中,五名学生依次跑到 \([2,5,4,6,3]\),则总代价为 |2-1|+|5-5|+|4-7|+|6-6|+|3-2|=5∣2?1∣+∣5?5∣+∣4?7∣+∣6?6∣+∣3?2∣=5。

在第二条命令中,五名学生依次跑到 \([4,5,7,6,3]\),则总代价为 |4-1|+|5-5|+|7-7|+|6-6|+|3-2|=4∣4?1∣+∣5?5∣+∣7?7∣+∣6?6∣+∣3?2∣=4。

在第三条命令中,三名学生依次跑到 \([11,10,9]\),则总代价为 |11-1|+|10-5|+|9-7|=17∣11?1∣+∣10?5∣+∣9?7∣=17。

在第四条命令中,三名学生依次跑到 \([4,2,3]\),则总代价为 |4-5|+|2-7|+|3-6|=9∣4?5∣+∣2?7∣+∣3?6∣=9。

在第五条命令中,三名学生依次跑到 \([7,6,5]\),则总代价为 |7-7|+|6-6|+|5-2|=3∣7?7∣+∣6?6∣+∣5?2∣=3。

对于10%的数据\(n,m \leq 10\)。

对于 \(40\%\) 的数据,\(n,m \leq 10^3\)。

对于 \(70\%\) 的数据,\(n,m \leq 10^5\)。

对于 \(100\%\) 的数据,\(n,m \leq 5 \times 10^5,1 \leq a_i,K \leq 10^6\)。

对于 \(100\%\) 的数据,学生休息的位置两两不同。

\(\color{#0066ff}{ 题解 }\)

不难想到用主席树维护

设当前人的范围为\([l,r]\),它们要到\([ql,qr]\)去

我们维护区间人的个数,这左子树有num个人,则可以递归\([l,mid][ql,ql+num-1]和[mid +1,r][ql+num,qr]\)

这复杂度。。。。\(O(n^2)\)啊。。。

进而考虑剪掉一些东西,比如\(r\leq ql, l\ge qr\)

拿\(r\le ql\)举个例子,让所有人到ql集合,然后一个一个往后走,就是个等差数列很好求

这样对于强数据还是会被卡

进而想一想,\(r\le qr\)能不能优化呢? 答案是肯定的

让所有人跑到qr,然后往回退体力,这种情况下每个人只会跑多不会跑少,所以可以直接减

分析一下这样做的复杂度

当\(mid \le ql +num-1\)时,左子树\(O(1)\)return,右子树\(O(logn)\)递归,反之同理

因此\(O(nlogn)\)可过!

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define LL long long
LL in() {
    char ch; LL x = 0, f = 1;
    while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    return x * f;
}
const int maxn = 2e6 + 100;
struct Tree {
protected:
    struct node {
        int num;
        LL tot;
        node *ch[2];
        node() { num = tot = 0; ch[0] = ch[1] = NULL; }
    };
    node *root[maxn];
    int len;
    void add(node *&o, node *lst, int l, int r, LL p) {
        o = new node();
        *o = *lst;
        o->num++, o->tot += p;
        if(l == r) return;
        int mid = (l + r) >> 1;
        if(p <= mid) add(o->ch[0], lst->ch[0], l, mid, p);
        else add(o->ch[1], lst->ch[1], mid + 1, r, p);
    }
    LL getsum(LL l, LL r) {
        return (r - l + 1) * (r - l) >> 1;
    }
    LL getans(node *x, node *y, LL l, LL r, LL ql, LL qr) {
        if(ql > qr) return 0;
        if(r <= qr) return qr * (qr - ql + 1) - (y->tot - x->tot) - getsum(ql, qr);
        if(l >= ql) return (y->tot - x->tot) - ql * (qr - ql + 1) - getsum(ql, qr);
        int num = y->ch[0]->num - x->ch[0]->num;
        int mid = (l + r) >> 1;
        return getans(x->ch[0], y->ch[0], l, mid, ql, ql + num - 1) + getans(x->ch[1], y->ch[1], mid + 1, r, ql + num, qr);
    }
public:
    void init(int n, LL *a) {
        len = 1e6 + 10;
        root[0] = new node();
        root[0]->ch[0] = root[0]->ch[1] = root[0];
        for(int i = 1; i <= n; i++) add(root[i], root[i - 1], 1, len, a[i]);
    }
    LL getans(int l, int r, int k) { return getans(root[l - 1], root[r], 1, len, k, k + r - l); }
}s;
LL a[maxn], n, m;
int main() {
    n = in(), m = in();
    for(int i = 1; i <= n; i++) a[i] = in();
    s.init(n, a);
    LL l, r, k;
    while(m --> 0) {
        l = in(), r = in(), k = in();
        printf("%lld\n", s.getans(l, r, k));
    }
    return 0;
}

原文地址:https://www.cnblogs.com/olinr/p/10376422.html

时间: 2024-08-11 11:45:23

P4559 [JSOI2018]列队的相关文章

P4559 [JSOI2018]列队 主席树

题意: 有n个人 每个人有其一开始所在的位置   有m个询问   l r k    问编号为l到r的人填满区间 k----k+r-l  需要的最少距离 很容易发现按照原来的相对位置来填k开始的位置肯定是一种最优解 lr的编号区间很容易想到主席树 所以问题转化为如何优化主席树的询问 1.如果遍历每个人 然后累和每个人的距离 肯定会超时  复杂度接近on 2.可以分三种情况讨论 如果所有的人都在   应到位置的左边  那么贡献为 如果所有的人都在    应到位置的右边 那么贡献为  否则的话递归到子

[JSOI2018]列队(主席树)

跟上次那道列队不一样,但都是九条可怜...(吉老师太强了) 在主席树上统计答案,因为值域只有 \(10^6\) 甚至不用离散化... \(Code\ Below:\) #include <bits/stdc++.h> #define int long long using namespace std; const int maxn=500000+10; const int lim=1000000; const int inf=0x3f3f3f3f; int n,m,a[maxn],Sum[ma

[JSOI2018]列队

题解 好像是\(JSOI2018\)最简单的一道题了,但是我还是做了好久== 所有人都往一个区间走可以转化为把编号为\([l,r]\)的人按照开始位置排序,然后排名为i的人走到\(k+i-1\)的位置的花费和 这样就是\(O(nmlogn)\)的了 那考虑用数据结构来优化这个过程 首先想到能不能用所有人的位置和-\((k+k+r-l+1)*(r-l+1)/2\) 显然不能,因为有些人会往左走,有些人会往右走 但我们可以发现在\([l,r]\)中一定有一个分界点,使得这个分界点往左的人都往右走,这

JSOI2018真题感悟

D1T1潜入行动: 大水题,可是本菜鸡手一抖MLE了,GG #include<iostream> #include<cstdio> #define llint long long int using namespace std; const llint maxn = 200005; const llint maxk = 107; const llint modd = 1e9+7; llint n, k; struct atree {//choose safe int dp[1000

BZOJ2720: [Violet 5]列队春游

2720: [Violet 5]列队春游 Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 173  Solved: 125[Submit][Status][Discuss] Description Input Output Sample Input Sample Output HINT 题解:对于这种题目我只能呵呵一笑欺负我是单身汪,哎! 一. 二.枚举每个位置,在枚举每个人,枚举每个人由当前位置可以看到的地方 于是: 于是我们如代码转移即可!p为当

循环列队的循序结构

</pre><pre name="code" class="cpp">//1.队列顺序结构的定义 #define MAXQSIZE 100 typedef struct { QElemType base[MAXQSIZE];//静态数组 int front;//队列头指针 int rear;//队列尾指针 }SqQueue; //解决队列的假溢出方法 //1.将循序列队臆造为一个环状空间.尾指针指向头指针 //2.在对满的情况下,rear指针

并发系统数据细节-列队

列队数据结构图形 stC代码实现,PHP程序需要懂C代码这是基础哈 #include<stdio.h> #include<stdlib.h> #include<memory.h> #define N 100 //定义常量N 为10 #define mytype int //定义常量mytype 替换int struct MyQueue { mytype data[N];//数组存储队列 int front;//拉屎  定义队头 int rear;//吃东西 定义队尾 }

C#操作消息列队

首先安装消息队列MSMQ,在"计算机管理-服务和应用程序-消息队列-专用队列"中新建列队名称Demo: static void SendAndReceiveMsg() { MessageQueue mq =new MessageQueue(); mq.Path = @".\Private$\Demo"; //构造消息 Message msg =new Message(); msg.Body ="Hello MessageQueue"; //向队列

消息列队组件的一些特性比较

RabbitMQ 基于AMQP实现,传统的messaging queue系统实现,基于Erlang.老牌MQ产品了.AMQP协议更多用在企业系统内,对数据一致性.稳定性和可靠性要求很高的场景,对性能和吞吐量还在其次. Kafka linkedin开源的MQ系统,主要特点是基于Pull的模式来处理消息消费,追求高吞吐量,一开始的目的就是用于日志收集和传输,0.8开始支持复制,不支持事务,适合产生大量数据的互联网服务的数据收集业务. ZeroMQ 只是一个网络编程的Pattern库,将常见的网络请求