zoukankan      html  css  js  c++  java
  • 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 imes 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))啊。。。

    进而考虑剪掉一些东西,比如(rleq ql, lge qr)

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

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

    进而想一想,(rle 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
    ", s.getans(l, r, k));
    	}
    	return 0;
    }
    
  • 相关阅读:
    BZOJ 1016 最小生成树计数(矩阵树定理)
    sdoi2013 spring(hash+容斥)
    51nod 1301 集合异或和(DP)
    51nod 1576 Tree and permutation(树的重心+dfn序)
    BZOJ 4145 [AMPPZ2014]The Prices (状压DP)
    BZOJ 2260 商店购物(最小树形图)
    BZOJ 4006 [JLOI2015]管道连接(斯坦纳树+子集DP)
    BZOJ 2595 [Wc2008]游览计划(斯坦纳树)
    BZOJ 5180 [Baltic2016]Cities(斯坦纳树)
    51nod 1392 装盒子(费用流)
  • 原文地址:https://www.cnblogs.com/olinr/p/10376422.html
Copyright © 2011-2022 走看看