HDU 5828 Rikka with Sequence

本文介绍了一种处理数组区间操作的有效算法,包括加法、开平方和求和操作。通过维护区间最大值、最小值及其数量,算法能在大量操作下保持高效。

题意: 对一个长度为10W 的数组进行如下三种操作:

1,区间[l,r]的数都加上x;

2,区间内的没个数都开根号(向下取整);

3,求区间[l,r]每个元素的和。

官方题解:

但是官方标程也被卡T了。据说hack数据是:10万个2,3,2,3,2,3.......,10万个操作 加6,开根。但是题的确不错。

在本题中我并没有按照官方题解的方法。我们可以想下,发现区间的最大值,最小值的差值会随着开方的次数越来越小。再加上又是区间内的数都加上一个值。

那么我们可以只需要考虑区间内极差小于2的区间即可。刚好我们需要维护区间最大值,最小值。

那么当区间的极差为0时,表示区间内的数都相同,直接开方,该区间的值都覆盖为sqrt(maxx); 区间的最大值,最小值,和,最大值的数量,最小值的数量自然就出来了

当区间的极差为1时,很显然最大值,最小值,及其它们的数量就表示出了区间的所有数。最大值最小值,自然也出来了。

a[i]<10W 那么对整个区间内的每个数开方的次数就很少了。 当开了一定次方之后整个区间内的数的差值就很接近了。

代码如下:

#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<vector>
#include<string.h>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<stdlib.h>
#include<time.h>
#include<set>
#include<math.h>
using namespace std;
typedef long long ll;
const int nn = 600100;
const int inf = (1 << 31);
const ll mod = 998244353;
const ll g = 3;//mod的原根,当且仅当 g^(MOD-1) = 1 % MOD
#define pb push_back
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1

struct FastIO {
	static const int S = 1310720;
	int wpos; char wbuf[S];
	FastIO() : wpos(0) {}
	inline int xchar() {
		static char buf[S];
		static int len = 0, pos = 0;
		if (pos == len)
			pos = 0, len = fread(buf, 1, S, stdin);
		if (pos == len) return -1;
		return buf[pos++];
	}
	inline int xuint() {
		int c = xchar(), x = 0;
		while (c <= 32) c = xchar();
		for (; '0' <= c && c <= '9'; c = xchar()) x = x * 10 + c - '0';
		return x;
	}
	inline int xint() {
		int s = 1, c = xchar(), x = 0;
		while (c <= 32) c = xchar();
		if (c == '-') s = -1, c = xchar();
		for (; '0' <= c && c <= '9'; c = xchar()) x = x * 10 + c - '0';
		return x * s;
	}
	inline ll xll() {
		ll s = 1, c = xchar(), x = 0;
		while (c <= 32) c = xchar();
		if (c == '-') s = -1, c = xchar();
		for (; '0' <= c && c <= '9'; c = xchar()) x = x * 10 + c - '0';
		return x * s;
	}
	inline void xstring(char *s) {
		int c = xchar();
		while (c <= 32) c = xchar();
		for (; c > 32; c = xchar()) *s++ = c;
		*s = 0;
	}
	inline void wchar(int x) {
		if (wpos == S) fwrite(wbuf, 1, S, stdout), wpos = 0;
		wbuf[wpos++] = x;
	}
	inline void wint(int x) {
		if (x < 0) wchar('-'), x = -x;

		char s[24];
		int n = 0;
		while (x || !n) s[n++] = '0' + x % 10, x /= 10;
		while (n--) wchar(s[n]);
	}
	inline void wstring(const char *s) {
		while (*s) wchar(*s++);
	}
	~FastIO() {
		if (wpos) fwrite(wbuf, 1, wpos, stdout), wpos = 0;
	}
} io;
struct node
{
	ll minn, maxx,sum;//区间最小值,最大值,区间和
	ll maxnum, minnum;//区间最大值,最小值的数量
	ll all;//区间覆盖为all,0表示没覆盖
	ll lazy;//区间内的数都加上lazy;
};
int n, m;
node t[nn << 2];

void pushup(int rt)
{
	t[rt].sum = t[rt << 1].sum + t[rt << 1 | 1].sum;
	t[rt].maxx = max(t[rt << 1].maxx, t[rt << 1 | 1].maxx);
	t[rt].minn = min(t[rt << 1].minn, t[rt << 1 | 1].minn);
	t[rt].maxnum = t[rt].minnum = 0;
	if (t[rt].maxx == t[rt << 1].maxx) t[rt].maxnum += t[rt << 1].maxnum;
	if (t[rt].maxx == t[rt << 1 | 1].maxx) t[rt].maxnum += t[rt << 1 | 1].maxnum;
	if (t[rt].minn == t[rt << 1].minn) t[rt].minnum += t[rt << 1].minnum;
	if (t[rt].minn == t[rt << 1 | 1].minn) t[rt].minnum += t[rt << 1 | 1].minnum;
}

void pushdown(int rt,int l,int r)
{
	int mid = (l + r) >> 1;
	if (t[rt].all)//区间覆盖
	{
		t[rt << 1].all = t[rt << 1 | 1].all = t[rt].all;
		t[rt << 1].sum = (ll)(mid - l + 1)*t[rt].all;
		t[rt << 1 | 1].sum = (ll)(r - mid)*t[rt].all;
		t[rt << 1].minn = t[rt << 1].maxx = t[rt << 1 | 1].maxx = t[rt << 1 | 1].minn = t[rt].all;
		t[rt << 1].minnum = t[rt << 1].maxnum = (ll)(mid - l + 1);
		t[rt << 1 | 1].maxnum = t[rt << 1 | 1].minnum = (ll)(r - mid);
		t[rt << 1].lazy = t[rt << 1 | 1].lazy = 0;
		t[rt].all = 0;
	}
	if (t[rt].lazy)//区间+lazy
	{
		t[rt << 1].lazy += t[rt].lazy;
		t[rt << 1 | 1].lazy += t[rt].lazy;
		t[rt << 1].sum += (ll)(mid - l + 1)*t[rt].lazy;
		t[rt << 1 | 1].sum += (ll)(r - mid)*t[rt].lazy;
		t[rt << 1].minn += t[rt].lazy;
		t[rt << 1 | 1].minn += t[rt].lazy;
		t[rt << 1].maxx += t[rt].lazy;
		t[rt << 1 | 1].maxx += t[rt].lazy;
		t[rt].lazy = 0;
	}
}

void build(int l, int r, int rt)
{
	t[rt].all = t[rt].lazy = 0;
	if (l == r)
	{
		t[rt].sum = t[rt].maxx = t[rt].minn = io.xll();
		t[rt].maxnum = t[rt].minnum = 1;
		return;
	}
	int mid = (l + r) >> 1;
	build(lson);
	build(rson);
	pushup(rt);
}

void add(int l, int r, int rt, int L, int R, ll x)
{
	if (L <= l && r <= R)
	{
		t[rt].sum += (ll)(r - l + 1)*x;
		t[rt].maxx += x;
		t[rt].minn += x;
		t[rt].lazy += x;
		return;
	}
	pushdown(rt, l, r);
	int mid = (l + r) >> 1;
	if (L <= mid) add(lson, L, R, x);
	if (R > mid) add(rson, L, R, x);
	pushup(rt); 
}
void t_sqrt(int l, int r, int rt, int L, int R)
{
	if (L <= l && r <= R && (t[rt].maxx - t[rt].minn) <= 1)
	{
		if (t[rt].maxx == 1) return;
		ll x = t[rt].maxx;
		if (t[rt].maxx == t[rt].minn)
		{
			t[rt].minn = t[rt].maxx = floor(sqrt(x));
			t[rt].lazy += t[rt].maxx-x;
			t[rt].sum = (ll)(r - l + 1)*t[rt].maxx;
		}
		else
		{
			t[rt].maxx = floor(sqrt(t[rt].maxx));
			t[rt].minn = floor(sqrt(t[rt].minn));
			if (t[rt].maxx == t[rt].minn)
			{
				t[rt].maxnum = t[rt].minnum = (ll)(r - l + 1);
				t[rt].lazy = 0;
				t[rt].all = t[rt].maxx;
				t[rt].sum = t[rt].maxx*t[rt].maxnum;
			}
			else
			{
				t[rt].lazy += t[rt].maxx - x;
				t[rt].sum = t[rt].maxx*t[rt].maxnum + t[rt].minn*t[rt].minnum;
			}
		}
		return;
	}
	int mid = (l + r) >> 1;
	pushdown(rt, l, r);
	if (L <= mid) t_sqrt(lson, L, R);
	if (R > mid) t_sqrt(rson, L, R);
	pushup(rt);
}

ll query(int l, int r, int rt, int L, int R)
{
	if (L <= l && r <= R)
	{
		return t[rt].sum;
	}
	int mid = (l + r) >> 1;
	pushdown(rt, l, r);
	ll ans = 0;
	if (L <= mid) ans += query(lson, L, R);
	if (R > mid) ans += query(rson, L, R);
	return ans;
}
int main()
{
	int t = io.xint();
	while (t--)
	{
		n = io.xll();
		m = io.xll();
		build(1,n,1);
		while (m--)
		{
			int op = io.xint();
			int l = io.xint();
			int r = io.xint();
			if (op == 1)
			{
				ll x = io.xll();
				add(1, n, 1, l, r, x);
			}
			else if (op == 2)
			{
				t_sqrt(1, n, 1, l, r);
			}
			else
			{
				printf("%lld\n", query(1, n, 1, l, r));
			}
		}
	}
	return 0;
}

加不加输入外挂都是能过的。但是必须要交G++ 不然就算加了输入外挂一样TLE

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值