首页 > 代码库 > 【bzoj3211】花神游历各国 并查集+树状数组

【bzoj3211】花神游历各国 并查集+树状数组

原文地址:http://www.cnblogs.com/GXZlegend/p/6809714.html


题目描述

 技术分享

输入

 技术分享

输出

每次x=1时,每行一个整数,表示这次旅行的开心度

样例输入

4
1 100 5 5
5
1 1 2
2 1 2
1 1 2
2 2 3
1 1 4

样例输出

101
11
11


题解

并查集+树状数组,附带一点数学知识

因为√1=1,且一个数x开接近log2(log2x)次平方后就会变成1,这个数是非常小的。

所以我们完全可以暴力修改,只需要知道每次的修改区间中有多少真正需要修改的数即可。

这可以用并查集来维护,b[i]表示i之后(包括i)第一个点权不为1的数的位置,当然还需要一个find函数。

这样暴力修改,但是不能暴力求和,还需要用一个树状数组来维护区间和。

注意开long long,并且把b[n+1]赋为n+1以防止越界。

#include <cstdio>
#include <cmath>
#include <algorithm>
#define N 100010
using namespace std;
typedef long long ll;
int n , b[N];
ll w[N] , f[N];
int find(int x)
{
	return b[x] == x ? x : b[x] = find(b[x]);
}
void update(int x , ll a)
{
	int i;
	for(i = x ; i <= n ; i += i & -i) f[i] += a;
}
ll query(int x)
{
	int i;
	ll ans = 0;
	for(i = x ; i ; i -= i & -i) ans += f[i];
	return ans;
}
int main()
{
	int m , i , opt , l , r;
	ll tmp;
	scanf("%d" , &n);
	for(i = 1 ; i <= n ; i ++ )
		scanf("%lld" , &w[i]) , b[i] = (w[i] <= 1 ? i + 1 : i) , update(i , w[i]);
	b[n + 1] = n + 1;
	scanf("%d" , &m);
	while(m -- )
	{
		scanf("%d%d%d" , &opt , &l , &r);
		if(opt == 1) printf("%lld\n" , query(r) - query(l - 1));
		else
		{
			for(i = find(l) ; i <= r ; i = find(i + 1))
			{
				tmp = (ll)sqrt(w[i]) , update(i , tmp - w[i]) , w[i] = tmp;
				if(w[i] <= 1) b[i] = find(i + 1);
			}
		}
	}
	return 0;
}

 

【bzoj3211】花神游历各国 并查集+树状数组