请客

题意:

给出一个长度为 n (<=100000) 的序列,有q次操作,每次操作将最大的K个数取出来求和,然后将这K个数减1,再放回序列,求最后的答案。

 

题解:

不难想首先将序列从小到大排序,从最后取出K个数,然后区间求和,区间减1,可以发现很容易保证这个序列的单调性。

比如n = 6 K = 2 序列为 1 2 3 4 4 5 那么就应该修改成这样 1 2 3 3 4 4,那么就应该找出K个数中最小的那个数的前驱后继。

 

代码:

#include <bits/stdc++.h>
using namespace std;

#define LL long long
const int N = 1e5 + 7;
LL sum[N << 2], lz[N << 2], ans, mif[N << 2];
int n, ai[N], K, q;

#define mid (l + r >> 1)
#define lson o << 1, l, mid
#define rson o << 1 | 1, mid + 1, r

void build (int o, int l, int r)
{
	if (l == r) 
	{
		sum[o] = ai[l];
		return;
	}
	build(lson);
	build(rson);
	sum[o] = sum[o << 1] + sum[o << 1 | 1];
}

void pushdown (int o, int l, int r)
{
	sum[o << 1] += (mid - l + 1) * lz[o];
	mif[o << 1] += (mid - l + 1) * lz[o];
	lz[o << 1] += lz[o];
	sum[o << 1 | 1] += (r - mid) * lz[o];
	mif[o << 1 | 1] += (r - mid) * lz[o];
	lz[o << 1 | 1] += lz[o];
	lz[o] = 0;
}

void update (int o, int l, int r, int L, int R, int x)
{
	if (l == L && r == R) 
	{
		lz[o] += x;
		mif[o] += (R - L + 1) * x;
		sum[o] += (R - L + 1) * x;
		return;
	}
	pushdown(o, l, r);
	if (R <= mid) update(lson, L, R, x);
	else if (L > mid) update(rson, L, R, x);
	else update(lson, L, mid, x), update(rson, mid + 1, R, x);
	sum[o] = sum[o << 1] + sum[o << 1 | 1];
	mif[o] = mif[o << 1] + mif[o << 1 | 1];
}

LL query (int o, int l, int r, int L, int R)
{
	if (l == L && r == R) return sum[o];
	pushdown(o, l, r);
	if (R <= mid) return query (lson, L, R);
	else if (L > mid) return query (rson, L, R);
	else return query (lson, L, mid) + query (rson, mid + 1, R);
}

int lazy (int o, int l, int r, int x)
{
	if (l == r && l == x) return mif[o];
	pushdown (o, l, r);
	if (x <= mid) return lazy (lson, x);
	else if (x > mid) return lazy (rson, x);
}

int find (int x)
{
	int l = 1, r = n;
	while (l < r)
	{
		if (ai[mid] + lazy(1, 1, n, mid) >= x) r = mid;
		else l = mid + 1;
	}
	return l;
}

int main () 
{
	scanf ("%d", &n);
	for (int i = 1; i <= n; ++i) scanf ("%d", &ai[i]);
	sort (ai + 1, ai + 1 + n);
	build (1, 1, n);
	scanf ("%d", &q);
	while (q--)
	{
		scanf ("%d", &K);
		int w = ai[n - K + 1] + lazy(1, 1, n, n - K + 1);
		int x = find (w), y = find (w + 1);
		if (ai[x] + lazy(1, 1, n, x) != ai[y] + lazy(1, 1, n, y))
		{
			ans += query (1, 1, n, x, x + K - n + y - 2);
			ans += query (1, 1, n, y, n);
			update (1, 1, n, x, x + K - n + y - 2, -1);
			update (1, 1, n, y, n, -1);
		}
		else 
		{
			ans += query (1, 1, n, x, x + K - 1);
			update (1, 1, n, x, x + K - 1, -1);
		}
	}
	cout << ans << endl;
	return 0;
}

  

总结:

善于观察因为只减1,很容易就保证了序列的单调性呀~

转载于:https://www.cnblogs.com/xgtao/p/6018812.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值