欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

[Jsoi2018]军训列队 主席树

程序员文章站 2024-03-03 16:45:04
...

Description
一共有nn个学生,第ii个学生的休息位置是a[i]a[i]。每一次命令,教官会指定一个区间[l,r][l, r]和集合点kk,所有编号在[l,r][l, r]内的学生都必须赶到集合点列队。在列队时,每一个学生需要选择[k,k+rl][k, k+r-l]中的一个整数坐标站定且不能有任何两个学生选择的坐标相同。学生从坐标xx跑到坐标yy需要耗费体力xy|x − y|
在一天的训练中,教官一共发布mm条命令ll, rr, kk,现在你需要计算对于每一条命令,在所有可能的列队方案中,消耗的体力值总和最小是多少。


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


Sample Output
5
4
17
9
3


一个比较显然的结论就是a[1]a[1]跑位置11a[2]a[2]跑位置22…然后以一个点作为分界点,肯定是一堆人向右跑,一堆人向左跑。
考虑在主席树上二分这个东西。
在当前区间中,midmid之前有cntcnt个人,
假设k+cnt1>midk+cnt-1>mid,因为mid>=mid>=左区间最后一个元素,所以左区间的人一定都会向右跑,到右区间中继续递归中。
假如k+cnt1<midk+cnt-1<mid,因为右边最小的元素不会小于mid+1mid+1,所以k+cnt<mid+1k+cnt<mid+1,也就是说右边最小的元素向左跑,换言之,右边所有人都向左跑。
假如k+cnt1=midk+cnt-1=mid
这时,假设右边第一个元素是mid+1mid+1,那么k+cnt=mid+1k+cnt=mid+1,也就是说mid+1mid+1这个位置的人原地不动,他成为了分界点,不用继续递归,
假设右边第一个元素大于midmid,那么k+cnt<k+cnt<右边第一个元素,我们再假设左边最后一个元素是midmid,那么以midmid为分界点,不用继续递归,再假设左边最后一个元素小于midmidk+cnt1>k+cnt-1>左边第一个元素
此时k+cnt1>k+cnt-1>左边第一个元素k+cnt<k+cnt<右边第一个元素,即左边的人向右跑,右边的人向左跑,不用继续递归。


#include <ctime>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>

using namespace std;
typedef long long LL;
int _max(int x, int y) {return x > y ? x : y;}
int _min(int x, int y) {return x < y ? x : y;}
const int N = 500001;
int read() {
	int s = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * f;
}
void put(LL x) {
	if(x >= 10) put(x / 10);
	putchar(x % 10 + '0');
}

struct tnode {
	int lc, rc, c; LL s;
} t[N * 20]; int cnt, rt[N];
LL lcnt, ls;

void Link(int &u, int l, int r, int p) {
	if(!u) u = ++cnt;
	t[u].c++, t[u].s += p;
	if(l == r) return ;
	int mid = (l + r) / 2;
	if(p <= mid) Link(t[u].lc, l, mid, p);
	else Link(t[u].rc, mid + 1, r, p);
}

void Merge(int &u1, int u2) {
	if(!u1 || !u2) {u1 = u1 + u2; return ;}
	t[u1].c += t[u2].c, t[u1].s += t[u2].s;
	Merge(t[u1].lc, t[u2].lc);
	Merge(t[u1].rc, t[u2].rc);
}

void query(int u1, int u2, int l, int r, int k) {
	if(l == r) return ;
	int mid = (l + r) / 2;
	LL c = t[t[u1].lc].c - t[t[u2].lc].c, s = t[t[u1].lc].s - t[t[u2].lc].s;
	if(mid - k + 1 < c + lcnt) lcnt += c, ls += s, query(t[u1].rc, t[u2].rc, mid + 1, r, k);
	else if(mid - k + 1 == c + cnt) lcnt += c, ls += s;
	else query(t[u1].lc, t[u2].lc, l, mid, k);
}

LL solve(int l, int r, int k) {
	lcnt = ls = 0;
	LL cnt = t[rt[r]].c - t[rt[l - 1]].c, s = t[rt[r]].s - t[rt[l - 1]].s;
	query(rt[r], rt[l - 1], 0, 1000000, k);
	LL rcnt = cnt - lcnt, rs = s - ls;
	LL ans = (lcnt + k + k - 1) * lcnt / 2 - ls + rs - (k + lcnt + k + cnt - 1) * rcnt / 2;
	return ans;
}

int main() {
	int n = read(), m = read();
	for(int i = 1; i <= n; i++) Link(rt[i], 0, 1000000, read()), Merge(rt[i], rt[i - 1]);
	for(int i = 1; i <= m; i++) {
		int l = read(), r = read(), k = read();
		put(solve(l, r, k)), puts("");
	} return 0;
}

相关标签: 主席树