【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;
}
时间: 2024-08-03 21:53:58

【bzoj3211】花神游历各国 并查集+树状数组的相关文章

BZOJ 3038 上帝造题的七分钟2 (并查集+树状数组)

题解:同 BZOJ 3211 花神游历各国,需要注意的是需要开long long,还有左右节点需要注意一下. #include <cstdio> #include <cmath> #include <iostream> #include <algorithm> using namespace std; typedef long long LL; LL a[100005],c[100005]; int f[100005],n,m,op,l,r,t; int s

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

Description Input Output 每次x=1时,每行一个整数,表示这次旅行的开心度 Sample Input 4 1 100 5 5 5 1 1 2 2 1 2 1 1 2 2 2 3 1 1 4 Sample Output 101 11 11 [分析] 开始看一眼觉得线段树可做. 后来看题解用树状数组瞬秒......orzzz,注意到任何一个int都可以在很小的次数下变为1,所以直接暴力单点修改,将变成1的数parent设为它右边的数. 注意,输入中可能会有很多的0....直接

[BZOJ 3211]花神游历各国(并查集+树状数组)

Description Solution 树状数组单点修改区间查询 我们知道一个数n最多修改loglogn次就会变为1 并查集维护每个数右边第一个不为1的位置 #include<cstdio> #include<iostream> #include<cstdlib> #include<cstring> #include<cmath> #define MAXN 100005 using namespace std; typedef long lon

Hdu 5458 Stability (LCA + 并查集 + 树状数组 + 缩点)

题目链接: Hdu 5458 Stability 题目描述: 给出一个还有环和重边的图G,对图G有两种操作: 1 u v, 删除u与v之间的一天边 (保证这个边一定存在) 2 u v, 查询u到v的路径上有几条桥. 解题思路: 这个题目有很多次操作,包含查询和删边两类,首先想到的是连通分量加缩点.如果按照顺序来,删边时候求桥就是问题了.所以可以离线处理,然后一边记录答案一边加边缩点. 对于一个图,把连通分量缩成一个点后,这个图就成为了一棵树, 然后深度差就等于桥的数目.查询的时候对于(u, v)

HDU 5458 Stability(双连通分量+LCA+并查集+树状数组)(2015 ACM/ICPC Asia Regional Shenyang Online)

题目大意:给一个N个点M条边的无向图,有Q个询问:1.删掉a.b之间所存在的边:2.询问有多少条边,单独删掉之后a与b不再连通. 思路:脑洞大开. 对于询问,首先想到的就是a与b之间有多少桥(割边),然后想到双连通分量,然而删边是个坑爹的问题,于是我们离线倒着来,把删边变成加边. 双连通分量这种东西呢,其实缩点连起来之后,就是一棵树辣. 然后询问两个点的时候,设根到点x的距离为dep[x],a.b的最近公共祖先为lca(a, b),那么询问query(a, b) = dep[a] + dep[b

[BZOJ3211]花神游历各国&amp;&amp;[BZOJ3038] 上帝造题的七分钟2 树状数组+并查集

3211: 花神游历各国 Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 4057  Solved: 1480[Submit][Status][Discuss] Description Input Output 每次x=1时,每行一个整数,表示这次旅行的开心度 Sample Input 4 1 100 5 5 5 1 1 2 2 1 2 1 1 2 2 2 3 1 1 4 Sample Output 101 11 11 HINT 对于100%的数据,

bzoj3211花神游历各国 线段树

3211: 花神游历各国 Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 4252  Solved: 1547[Submit][Status][Discuss] Description Input Output 每次x=1时,每行一个整数,表示这次旅行的开心度 Sample Input 41 100 5 551 1 22 1 21 1 22 2 31 1 4 Sample Output 1011111 HINT 对于100%的数据, n ≤ 1000

bzoj3211: 花神游历各国

/*向下取整smg! Popoqqq:题目大意:给定一个序列,提供下列操作:1.将[l.r]区间内每个数a[i]变为sqrt(a[i])2.查询[l,r]区间的和根号是不支持区间修改的,于是我们选择单点修改区间查询的树状数组,但是这样是O(n^2)的,怎么办?我们发现一个数x最多开loglogx次根号就会变为1 也就是一个int范围内的数只要开6次根号就会变为1 于是修改的总时间复杂度为O(nloglogn)但是单次修改怎么办?我们维护一个并查集,一旦一个数为1或0,我们就把这个位置的fathe

bzoj3211: 花神游历各国(线段树)

bzoj3211 输出格式:每次x=1时,每行一个整数,表示这次旅行的开心度 解析:已经写了好几次这种题了,这次来水水博客 QAQ~ ???不难发现对于数列中的每一个数,被进行操作后改变的次数是很少的. ???于是可以记录下每一段区间是否已经全部变成了1(变成1后再进行操作数仍不变),对于全是1的区间不用再进行操作. ???这样我们可以对每一个不全是1的区间暴力修改,最后统计一下和即可. 代码如下: #include<cstdio> #include<algorithm> #inc