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

来源:互联网 发布:淘宝销量怎么查看 编辑:程序博客网 时间:2024/06/05 07:17

题目链接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 longusing 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[N],a[N],f[N];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;}
阅读全文
1 0
原创粉丝点击