题目大意:花神对每一个国家有一个喜爱程度,有的时候他会对连续的一段国家进行访问,求他的喜爱程度的和;有的时候他会对连续的一段国家产生厌恶,喜爱程度变成sqrt(x)下取整。
思路:乍一看好像是RMQ问题,用线段树就可以水过,但是开根号的标记怎么下传?这是一个严重的问题,所以我们要换一个思路。
注意到开根号有一个有趣的性质:sqrt(1) = 1,sqrt(0) = 0,而且所有的数字经过有限次的开根号运算都会变成1。这个性质就很好了。我们对每一个点暴力开根号,然后当这个店的点权变成1的时候就打一个标记,下次不管这个点了。用线段树维护。
当然还有常数更小的方法。对整个序列维护树状数组,利用并查集维护每个数右边第一个不是1的数字,然后暴力开根号,当一个数字变成1的时候就把这个点在并查集中的父亲连到它右边的数的父亲上。在修改连续区间的时候就可以跳过连续的1了。
CODE:
思路:乍一看好像是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]);
}