[JSOI2018]列队

题解

好像是\(JSOI2018\)最简单的一道题了,但是我还是做了好久==

所有人都往一个区间走可以转化为把编号为\([l,r]\)的人按照开始位置排序,然后排名为i的人走到\(k+i-1\)的位置的花费和

这样就是\(O(nmlogn)\)的了

那考虑用数据结构来优化这个过程

首先想到能不能用所有人的位置和-\((k+k+r-l+1)*(r-l+1)/2\)

显然不能,因为有些人会往左走,有些人会往右走

但我们可以发现在\([l,r]\)中一定有一个分界点,使得这个分界点往左的人都往右走,这个分界点往右的人都往左走

那么求出这个分界点以后就可以直接算了

这个分界点要满足的条件就是\(排名为t的位置<=k+t-1\)

所以我们就可以考虑通过主席树来二分这个位置

注意一下主席树上二分的细节就可以辣

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
# define LL long long
# define ls (t[now].l)
# define rs (t[now].r)
const int M = 500005 ;
const int N = 1000005 ;
using namespace std ;
inline int read() {
    char c = getchar() ; int x = 0 , w = 1 ;
    while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
    while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
    return x*w ;
}

int n , m , tot , E = 1000000 , rt[M] , ret ;
struct Node { int l , r , size ; LL sum ; } t[N * 21] ;
void Build(int x , int l , int r , int &now) {
    t[++tot] = t[now] ; now = tot ; ++t[now].size ; t[now].sum += x ;
    if(l == r) return ; int mid = (l + r) >> 1 ;
    if(mid >= x) Build(x , l , mid , ls) ; else Build(x , mid + 1 , r , rs) ;
}
LL qsum(int i , int j , int L , int R , int l , int r) {
    if(l > R || r < L) return 0 ;
    if(l == L && r == R) return t[j].sum - t[i].sum ;
    int mid = (l + r) >> 1 ;
    if(mid >= R) return qsum(t[i]. l ,t[j].l , L , R , l , mid) ;
    else if(mid < L) return qsum(t[i].r , t[j].r , L , R , mid + 1 , r) ;
    else return qsum(t[i].l , t[j].l , L , mid , l , mid) + qsum(t[i].r , t[j].r , mid + 1 , R , mid + 1 , r) ;
}
void qkth(int i , int j , int k , int l , int r , int rnk) {
    if(t[j].size - t[i].size == 0) return ;
    if(l == r) {
        ++ rnk ;
        if(l <= k + rnk - 1) ret = rnk ;
        return ;
    }
    int mid = (l + r) >> 1 , lsz = t[t[j].l].size - t[t[i].l].size ;
    if(mid <= k + rnk + lsz - 1) {
        ret = rnk + lsz ;
        qkth(t[i].r , t[j].r , k , mid + 1 , r , rnk + lsz) ;
    }
    else qkth(t[i].l , t[j].l , k , l , mid , rnk) ;
}
int main() {
    n = read() ; m = read() ; E += n ;
    for(int i = 1 , x ; i <= n ; i ++) {
        x = read() ; rt[i] = rt[i - 1] ;
        Build(x , 0 , E , rt[i]) ;
    }
    int idl , idr , k , l , r , mid , sz ; LL Ans = 0 ;
    while(m --) {
        idl = read() ; idr = read() ; k = read() ; ret = Ans = 0 ; sz = idr - idl + 1 ;
        qkth(rt[idl - 1] , rt[idr] , k , 0 , E , 0) ;
        Ans += 1LL * (2LL * k + ret - 1) * ret / 2  - qsum(rt[idl - 1] , rt[idr] , 0 , k + ret - 1 , 0 , E) + qsum(rt[idl - 1] , rt[idr] , k + ret , E , 0 , E) - 1LL * (2LL * k + ret + sz - 1) * (sz - ret) / 2 ;
        printf("%lld\n",Ans) ;
    }
    return 0 ;
}

原文地址:https://www.cnblogs.com/beretty/p/10291797.html

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

[JSOI2018]列队的相关文章

[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

P4559 [JSOI2018]列队

\(\color{#0066ff}{ 题目描述 }\) 作为一名大学生,九条可怜在去年参加了她人生中的最后一次军训. 军训中的一个重要项目是练习列队,为了训练学生,教官给每一个学生分配了一个休息位置.每次训练开始前,所有学生都在各自的休息位置休息,但是当教官发出集合命令后,被点到的学生必须要到指定位置集合. 为了简化问题,我们把休息位置和集合位置抽象成一根数轴.一共有 \(n\) 个学生,第 \(i\) 个学生的休息位置是 \(a_i\).每一次命令,教官会指定一个区间 \([l,r]\) 和集

P4559 [JSOI2018]列队 主席树

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

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库,将常见的网络请求