您的位置:首页 > 其它

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

2017-08-20 21:52 681 查看
题目链接http://www.lydsy.com/JudgeOnline/problem.php?id=3211

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%的数据, n ≤ 100000,m≤200000 ,data[i]非负且小于10^9

题目大意:给定一个长度为n的序列,支持区间开平方(向下取整)和区间求和

根号是不支持区间修改的,我们可以选择单点修改区间查询的树状数组,但是这样是O(n^2)的,时间上接受不了

发现一个数x最多开loglogx次根号就会变为1,也就是一个int范围内的数最多开6次根号就会变为1 ,如果某个数变为1,以后可以不对这个数进行操作,修改的总时间复杂度为O(nloglog1e9)

对于每一个已经为0或1的点,以后的每次操作都可以直接跳过这个点,这种操作可以用并查集来实现(同BZOJ2054,疯狂的馒头)对于每一个非0非1的节点i,使f[i]=i,如果某次操作将编号为i的点变为1,则更改f[i]为i+1,在区间修改时,每次从节点i跳到f[i+1]即可

细节1:f[n+1]=n+1

细节2:答案会爆int

某sb这题因为没处理好细节T了15次,WA了3次,CE了1次…..

当然这题也可以用线段树打标记来实现。。但因为并查集好写,所以就不写线段树的做法了23333

代码如下:

#include<ctype.h>
#include<cstdio>
#include<cmath>
#define N 100500
#define int long long
using namespace std;
inline int read(){
int f=1,x=0;
char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return x*f;
}
int n,m,k,l,r,t,q
,a
,f
;
int lowbit(int x){return x&-x;}
void add(int x,int v){
while(x<=n){
q[x]=q[x]+v;
x=x+lowbit(x);
}
}
long long sum(int x){
long long tmp=0;
while(x){
tmp=tmp+q[x];
x=x-lowbit(x);
}
return tmp;
}
int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
main(){
n=read();
for(int i=1;i<=n;i++) a[i]=read(),add(i,a[i]),f[i]=a[i]<=1?i+1:i;
f[n+1]=n+1;
m=read();
for(int i=1;i<=m;i++){
k=read();l=read();r=read();
if(k==1) printf("%lld\n",sum(r)-sum(l-1));
else{
for(int j=l;j<=r;j=find(j+1)){
int tmp=(int)sqrt(a[j]);
add(j,tmp-a[j]);
a[j]=tmp;
if(a[j]<=1) f[j]=find(j+1);
}
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: