【题解】P4145

Link

考试的 D 题。

题意

\(n\) 个数和 \(m\) 个操作,每次操作有三个整数 \(k,l,r\)

  • k = 0:给 \([l,r]\) 中的每个数开平方根(向下取整)
  • k = 1:询问 \([l,r]\) 中各个数的和。

思路

标签:并查集、分块???不会……

我们用朴素的线段树。但如果直接暴力修改会 T 飞掉:Link(竟然还有40分)

我们需要剪枝!

可以发现若 \(n\le1\),则 \([\sqrt{n}]=[n]\),相当于这一部分不用处理。利用这个性质算法可以得到显著的优化,我们记录 \(maxx\) 为子树中的最大值,若修改时 \(maxx\le1\) 则忽略。

具体细节见代码。

\(\text{Code}\)

(记得开 long long

#include <iostream>
#include <cstdio>
#include <cmath>
#define int long long
#define lson pos << 1
#define rson pos << 1 | 1
using namespace std;

const int MAXN = 1e5 + 5;

int n, m;

struct tree
{
	int l, r, sum, maxx;
}t[MAXN << 2];

int max(int x, int y)
{
	if (x >= y)
	{
		return x;
	}
	return y;
}

void pushup(int pos)
{
	t[pos].sum = t[lson].sum + t[rson].sum;
	t[pos].maxx = max(t[lson].maxx, t[rson].maxx);
}

void build(int l, int r, int pos)
{
	t[pos].l = l, t[pos].r = r;
	if (l == r)
	{
		scanf("%lld", &t[pos].sum);
		t[pos].maxx = t[pos].sum;
		return;
	}
	int mid = (l + r) >> 1;
	build(l, mid, lson);
	build(mid + 1, r, rson);
	pushup(pos);
}

void update(int L, int R, int pos)
{
	int l = t[pos].l, r = t[pos].r;
	if (l == r) //到叶子节点时修改
	{
		t[pos].sum = sqrt(t[pos].sum);
		t[pos].maxx = sqrt(t[pos].maxx);
		return;
	}
	int mid = (l + r) >> 1;
	if (L <= mid && t[lson].maxx > 1) //大于1时修改
	{
		update(L, R, lson);
	}
	if (R > mid && t[rson].maxx > 1)
	{
		update(L, R, rson);
	}
	pushup(pos);
}

int query(int L, int R, int pos)
{
	int l = t[pos].l, r = t[pos].r;
	if (l >= L && r <= R)
	{
		return t[pos].sum;
	}
	int mid = (l + r) >> 1, res = 0;
	if (L <= mid)
	{
		res = query(L, R, lson);
	}
	if (R > mid)
	{
		res += query(L, R, rson);
	}
	return res;
}

signed main()
{
	scanf("%lld", &n);
	build(1, n, 1);
	scanf("%lld", &m);
	while (m--)
	{
		int k, l, r;
		scanf("%lld%lld%lld", &k, &l, &r);
		if (l > r) //细节!!!
		{
			swap(l, r);
		}
		if (k) //k = 1
		{
			printf("%lld\n", query(l, r, 1));
		}
		else //k = 0
		{
			update(l, r, 1);
		}
	}
	return 0;
}
posted @ 2021-08-07 17:56  mango09  阅读(40)  评论(0编辑  收藏  举报
-->