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

题目大意:花神对每一个国家有一个喜爱程度,有的时候他会对连续的一段国家进行访问,求他的喜爱程度的和;有的时候他会对连续的一段国家产生厌恶,喜爱程度变成sqrt(x)下取整。

思路:乍一看好像是RMQ问题,用线段树就可以水过,但是开根号的标记怎么下传?这是一个严重的问题,所以我们要换一个思路。

注意到开根号有一个有趣的性质:sqrt(1) = 1,sqrt(0) = 0,而且所有的数字经过有限次的开根号运算都会变成1。这个性质就很好了。我们对每一个点暴力开根号,然后当这个店的点权变成1的时候就打一个标记,下次不管这个点了。用线段树维护。

当然还有常数更小的方法。对整个序列维护树状数组,利用并查集维护每个数右边第一个不是1的数字,然后暴力开根号,当一个数字变成1的时候就把这个点在并查集中的父亲连到它右边的数的父亲上。在修改连续区间的时候就可以跳过连续的1了。

CODE:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 200010
using namespace std;

int cnt,asks;
int src[MAX];
long long fenwick[MAX];

int father[MAX];

void Pretreatment();

inline void Fix(int x,int c);
inline void Fix(int x);
inline long long GetSum(int x);

int Find(int x);

int main()
{
	cin >> cnt;
	for(int i = 1;i <= cnt; ++i) {
		scanf("%d",&src[i]);
		Fix(i,src[i]);
		if(src[i] <= 1)	father[i] = i + 1;
	}
	cin >> asks;
	for(int flag,x,y,i = 1;i <= asks; ++i) {
		scanf("%d%d%d",&flag,&x,&y);
		if(flag == 1)
			printf("%lld\n",GetSum(y) - GetSum(x - 1));
		else
			for(x = Find(x);x <= y;x = Find(x + 1)) {
				Fix(x,-src[x]);
				src[x] = sqrt(src[x]) + 1e-7;
				Fix(x,src[x]);
				if(src[x] == 1)	father[x] = Find(x + 1);
			}
	}
	return 0;
}

inline void Fix(int x,int c)
{
	for(int i = x;i <= cnt;i += i&-i)
		fenwick[i] += c;
}

inline long long GetSum(int x)
{
	long long re = 0;
	for(int i = x;i;i -= i&-i)
		re += fenwick[i];
	return re;
}

int Find(int x)
{
	if(!father[x] || father[x] == x)	return father[x]  = x;
	return father[x] = Find(father[x]);
}
时间: 2024-12-21 18:50:02

BZOJ 3211 花神游历各国 树状数组+并查集的相关文章

BZOJ 3211 花神游历各国 树状数组(线段树)+优化

题意:给你一段区间,然后每个点的初始值都告诉你,现有两种操作,一种是给你一个小区间的左右端点,之后把这个区间内的所有值都开根号,另一种就是区间求值. 方法:树状数组维护求和,巧妙开根号.(线段树) 解析:这道是某次考试考的题- -.当时也没想到快的开根号方法,暴力开根号好像70分吧. 首先要明确一个事情:被开根号的数最大是109,而109开几次会开到1呢?用计算器算一下发现5次就将这个最大的数开到1了,而1开根号怎么开都是1,所以如果再对其进行开根号操作,仅仅是无用的浪费时间了.所以怎么维护这种

【BZOJ3211】花神游历各国 树状数组 并查集 均摊分析

链接: #include <stdio.h> int main() { puts("转载请注明出处[vmurder]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/44686727"); } 题解: 一个点开几次方就没啦.所以我们只需要修改不是0或者1的点就行了. 均摊基本O(n). 然后用并查集维护一个点右边第一个不是0的数. 手写读入果然高大上.卡rank神器. 顺便Orz一下wys大神. 代

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

题解:首先,单点修改求区间和可以用树状数组实现,因为开平方很耗时间,所以在这个方面可以优化,我们知道,开平方开几次之后数字就会等于1 ,所以,用数组记录下一个应该开的数,每次直接跳到下一个不是1的数字进行开平方,至于这个数组,可以用并查集维护. #include <cstdio> #include <cmath> #include <iostream> using namespace std; typedef long long LL; LL c[100005]; in

BZOJ 3211 花神游历各国 线段树题解

BZOJ 3211 花神游历各国 线段树题解 3211: 花神游历各国 Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 2551  Solved: 946[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

Wikioi 2492 树状数组+并查集(单点更新区间查询)

刚开始做的时候用线段树做的,然后就跳进坑里了--因为要开方,所以区间的值都得全部变,然后想用lazy标记的,但是发现用不了,单点更新这个用不了,然后就不用了,就T了.然后实在不行了,看了别人的题解,原来是用树状数组+并查集的方法,唉--没想到啊! 因为开方之后多次那个数就会变成1了,所以是1的时候开方下去就没用了.树状数组更新的时候就把其更新的差更新即可,太机智了这题-- 昨天做了,然后出错找了好久都找不出来,原来是把s[i]写成c[i]了,然后答案一直错,晕-- #include <iostr

[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%的数据,

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

线段树...区间开方...明显是要处理到叶节点的 之前在CF做过道区间取模...差不多, 只有开方, 那么每个数开方次数也是有限的(0,1时就会停止), 最大的数10^9开方10+次也就不会动了.那么我们线段树多记个max就可以少掉很多不必要的操作 -------------------------------------------------------------------------------------------- #include<cstdio> #include<cs

[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

花神游历各国 题解(小清新线段树/树状数组+并查集)

众所周知,这是一道小清新线段树 然而可以用树状数组水过去且跑得飞快 看到区间开方第一反应肯定是线段树懒标记区间修改之类的,但是这个东西似乎确凿不可维护 所以考虑暴力循环单点修改->T飞 于是我们关注一下开方本身的特殊性 我们知道,如果每次向下取整,一个数经过多次操作最终会变成1(或0) 事实上,大概经过 log(logx)次就会变成1 这是什么概念呢?经过博主测试,1e9只要经过五次开方取整就会变成1 那么接下来就能够利用1每次不必再操作优化复杂度 可以维护一个类似链表的结构,指向下一个>1的