HDU 5828 Rikka with Sequence

本文介绍了一种使用线段树解决特定数学运算问题的方法。针对数组进行加法、平方根运算及求和操作,通过维护节点的最大最小值来优化平方根操作,并进一步处理极差为1的情况以应对更复杂的数据输入。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >



Problem Description
As we know, Rikka is poor at math. Yuta is worrying about this situation, so he gives Rikka some math tasks to practice. There is one of them:

Yuta has an array A with n numbers. Then he makes m operations on it. 

There are three type of operations:

1 l r x : For each i in [l,r], change A[i] to A[i]+x
2 l r : For each i in [l,r], change A[i] to A[i]
3 l r : Yuta wants Rikka to sum up A[i] for all i in [l,r]

It is too difficult for Rikka. Can you help her?
 

Input
The first line contains a number t(1<=t<=100), the number of the testcases. And there are no more than 5 testcases with n>1000.

For each testcase, the first line contains two numbers n,m(1<=n,m<=100000). The second line contains n numbers A[1]~A[n]. Then m lines follow, each line describe an operation.

It is guaranteed that 1<=A[i],x<=100000.
 

Output
For each operation of type 3, print a lines contains one number -- the answer of the query.
 

Sample Input
1 5 5 1 2 3 4 5 1 3 5 2 2 1 4 3 2 4 2 3 5 3 1 5
 

Sample Output
5 6
线段树的暴力更新,可以想象,sqrt操作的下降速度是很快的
于是每个节点维护最大和最小值,当sqrt操作是暴力的传递下去,当最大值和最小值的sqrt相同时就无需下传了。
#include<set>
#include<map>
#include<ctime>
#include<cmath>
#include<stack>
#include<queue>
#include<bitset>
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
#define rep(i,j,k) for (int i = j; i <= k; i++)
#define per(i,j,k) for (int i = j; i >= k; i--)
#define lson x << 1, l, mid
#define rson x << 1 | 1, mid + 1, r
#define fi first
#define se second
#define mp(i,j) make_pair(i,j)
#define pii pair<int,int>
using namespace std;
typedef long long LL;
const int low(int x) { return x&-x; }
const double eps = 1e-8;
const int INF = 0x7FFFFFFF;
const int mod = 1e9 + 7;
const int N = 4e5 + 10;
const int read()
{
	char ch = getchar();
	while (ch<'0' || ch>'9') ch = getchar();
	int x = ch - '0';
	while ((ch = getchar()) >= '0'&&ch <= '9') x = x * 10 + ch - '0';
	return x;
}
int T, n, m, l, r, x, type;

LL f[N], L[N], R[N], a[N];

int g(LL x)
{
	return (int)sqrt(1.0*x);
}

void up(int x)
{
	L[x] = min(L[x << 1], L[x << 1 | 1]);
	R[x] = max(R[x << 1], R[x << 1 | 1]);
	f[x] = f[x << 1] + f[x << 1 | 1];
}

void build(int x, int l, int r)
{
	a[x] = 0;
	if (l == r)
	{
		scanf("%d", &f[x]);
		R[x] = L[x] = f[x];
	}
	else
	{
		int mid = l + r >> 1;
		build(lson);    build(rson);
		up(x);
	}
}

void push(int x, int l, int r)
{
	if (a[x])
	{
		a[x << 1] += a[x];    L[x << 1] += a[x];    R[x << 1] += a[x];
		f[x << 1] += 1LL * a[x] * ((l + r >> 1) - l + 1);
		a[x << 1 | 1] += a[x];    L[x << 1 | 1] += a[x];    R[x << 1 | 1] += a[x];
		f[x << 1 | 1] += 1LL * a[x] * (r - (l + r >> 1));
		a[x] = 0;
	}
	if (L[x] == R[x])
	{
		a[x << 1] = a[x << 1 | 1] = 0;
		L[x << 1] = R[x << 1] = L[x << 1 | 1] = R[x << 1 | 1] = L[x];
		f[x << 1] = 1LL * L[x] * ((l + r >> 1) - l + 1);
		f[x << 1 | 1] = 1LL * R[x] * (r - (l + r >> 1));
	}
}

void add(int x, int l, int r, int ll, int rr, int v)
{
	if (l < r && (a[x] || L[x] == R[x])) push(x, l, r);
	if (ll <= l && r <= rr)
	{
		a[x] += v;    L[x] += v;    R[x] += v;
		f[x] += 1LL * v * (r - l + 1);
	}
	else
	{
		int mid = l + r >> 1;
		if (ll <= mid) add(lson, ll, rr, v);
		if (rr > mid) add(rson, ll, rr, v);
		up(x);
	}
}

void down(int x, int l, int r, int ll, int rr)
{
	if (l < r && (a[x] || L[x] == R[x])) push(x, l, r);
	if (ll <= l && r <= rr)
	{
		if (g(L[x]) == g(R[x]))
		{
			L[x] = R[x] = g(L[x]);
			f[x] = 1LL * L[x] * (r - l + 1);
		}
		else
		{
			int mid = l + r >> 1;
			if (ll <= mid) down(lson, ll, rr);
			if (rr > mid) down(rson, ll, rr);
			up(x);
		}
	}
	else
	{
		int mid = l + r >> 1;
		if (ll <= mid) down(lson, ll, rr);
		if (rr > mid) down(rson, ll, rr);
		up(x);
	}
}

LL sum(int x, int l, int r, int ll, int rr)
{
	if (l < r && (a[x] || L[x] == R[x])) push(x, l, r);
	if (ll <= l && r <= rr) return f[x];
	else
	{
		int mid = l + r >> 1;
		LL res = 0;
		if (ll <= mid) res += sum(lson, ll, rr);
		if (rr > mid) res += sum(rson, ll, rr);
		return res;
	}
}

int main()
{
	T = read();
	while (T--)
	{
		scanf("%d%d", &n, &m);
		build(1, 1, n);
		while (m--)
		{
			type = read(); l = read();	r = read();
			if (type == 1)
			{
				x = read();
				add(1, 1, n, l, r, x);
			}
			else
			{
				if (type == 2) down(1, 1, n, l, r);
				else printf("%lld\n", sum(1, 1, n, l, r));
			}
		}
	}
	return 0;
}

hdu上的数据已经加强了,上述版本会tle。
对于加强的数据,之所以超时的原因是数据中出现了sqrt以后仍旧不相等的情况,
举个例子,x和x+1,每次加上x^2+x,之后再开方,会保持原来的序列不变,这导致了暴力更新每次是真的暴力。
解决这种情况的办法就是再多考虑极差为1的情况,可以想象,对于一段区间连续的加操作和开放操作,
值之间的差一定会趋向于0或1,而不会是更大的数。
所以在原来的情况下加上判断极差为1的情况,此时的开方操作相当于区间减操作。
因为开方前相差1,开方后仍旧是相差1,这样就可以通过加强的数据了。
#include<set>
#include<map>
#include<ctime>
#include<cmath>
#include<stack>
#include<queue>
#include<bitset>
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
#define rep(i,j,k) for (int i = j; i <= k; i++)
#define per(i,j,k) for (int i = j; i >= k; i--)
#define lson x << 1, l, mid
#define rson x << 1 | 1, mid + 1, r
#define fi first
#define se second
#define mp(i,j) make_pair(i,j)
#define pii pair<int,int>
using namespace std;
typedef long long LL;
const int low(int x) { return x&-x; }
const double eps = 1e-8;
const int INF = 0x7FFFFFFF;
const int mod = 1e9 + 7;
const int N = 4e5 + 10;
const int read()
{
	char ch = getchar();
	while (ch<'0' || ch>'9') ch = getchar();
	int x = ch - '0';
	while ((ch = getchar()) >= '0'&&ch <= '9') x = x * 10 + ch - '0';
	return x;
}
int T, n, m, l, r, x, type;

LL f[N], L[N], R[N], a[N];

int g(LL x)
{
	return (int)sqrt(1.0*x);
}

void up(int x)
{
	L[x] = min(L[x << 1], L[x << 1 | 1]);
	R[x] = max(R[x << 1], R[x << 1 | 1]);
	f[x] = f[x << 1] + f[x << 1 | 1];
}

void build(int x, int l, int r)
{
	a[x] = 0;
	if (l == r)
	{
		R[x] = L[x] = f[x] = read();
	}
	else
	{
		int mid = l + r >> 1;
		build(lson);    build(rson);
		up(x);
	}
}

void push(int x, int l, int r)
{
	if (a[x])
	{
		a[x << 1] += a[x];    L[x << 1] += a[x];    R[x << 1] += a[x];
		f[x << 1] += 1LL * a[x] * ((l + r >> 1) - l + 1);
		a[x << 1 | 1] += a[x];    L[x << 1 | 1] += a[x];    R[x << 1 | 1] += a[x];
		f[x << 1 | 1] += 1LL * a[x] * (r - (l + r >> 1));
	}
	if (L[x] == R[x])
	{
		L[x << 1] = R[x << 1] = L[x << 1 | 1] = R[x << 1 | 1] = L[x];
		f[x << 1] = 1LL * L[x] * ((l + r >> 1) - l + 1);
		f[x << 1 | 1] = 1LL * R[x] * (r - (l + r >> 1));
	}
	a[x] = 0;
}

void add(int x, int l, int r, int ll, int rr, int v)
{
	if (l < r && (a[x] || L[x] == R[x])) push(x, l, r);
	if (ll <= l && r <= rr)
	{
		a[x] += v;    L[x] += v;    R[x] += v;
		f[x] += 1LL * v * (r - l + 1);
	}
	else
	{
		int mid = l + r >> 1;
		if (ll <= mid) add(lson, ll, rr, v);
		if (rr > mid) add(rson, ll, rr, v);
		up(x);
	}
}

void down(int x, int l, int r, int ll, int rr)
{
	if (l < r && (a[x] || L[x] == R[x])) push(x, l, r);
	if (ll <= l && r <= rr)
	{
		if (g(L[x]) == g(R[x]))
		{
			L[x] = R[x] = g(L[x]);
			f[x] = 1LL * L[x] * (r - l + 1);
		}
		else if (L[x] + 1 == R[x])
		{
			int k = R[x] - g(R[x]);
			add(x, l, r, ll, rr, -k);
		}
		else 
		{
			int mid = l + r >> 1;
			if (ll <= mid) down(lson, ll, rr);
			if (rr > mid) down(rson, ll, rr);
			up(x);
		}
	}
	else
	{
		int mid = l + r >> 1;
		if (ll <= mid) down(lson, ll, rr);
		if (rr > mid) down(rson, ll, rr);
		up(x);
	}
}

LL sum(int x, int l, int r, int ll, int rr)
{
	if (l < r && (a[x] || L[x] == R[x])) push(x, l, r);
	if (ll <= l && r <= rr) return f[x];
	else
	{
		int mid = l + r >> 1;
		LL res = 0;
		if (ll <= mid) res += sum(lson, ll, rr);
		if (rr > mid) res += sum(rson, ll, rr);
		return res;
	}
}

int main()
{
	T = read();
	while (T--)
	{
		scanf("%d%d", &n, &m);
		build(1, 1, n);
		while (m--)
		{
			type = read(); l = read();	r = read();
			if (type == 1)
			{
				x = read();
				add(1, 1, n, l, r, x);
			}
			else
			{
				if (type == 2) down(1, 1, n, l, r);
				else printf("%lld\n", sum(1, 1, n, l, r));
			}
		}
	}
	return 0;
}



评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值