[BZOJ3038]上帝造题的七分钟2 树状数组+并查集

来源:互联网 发布:qq群刷屏软件 编辑:程序博客网 时间:2024/05/14 04:47

考试的时候用了两个树状数组去优化,暴力修改,树状数组维护修改后区间差值还有最终求和,最后骗了40分。。

这道题有好多种做法,求和好说,最主要的是开方。这道题过的关键就是掌握一点:在数据范围内,最多开方五六次就会变成1,这样以后再修改就不用修改了。

①  线段树打标记

②  分块打标记

③  树状数组+并查集

因为我考试的时候用的树状数组,所以直接打的第三种,相对来说代码量也少一些。

 

思路:开始时父亲都指向自己,如果变成1,就把父亲指向下一个位置即可。修改的时候相当于跳着修改。代码当中会有注解。

#include<iostream>#include<cstdio>#include<cstring>#include<cmath>using namespace std;#define pos(i,a,b) for(int i=(a);i<=(b);i++)#define pos2(i,a,b) for(int i=(a);i>=(b);i--)#define N 201000#define LL long longint n,m;LL c[N],cha[N];LL a[N];LL fa[N];LL lowbit(int x){    return x&(-x);}void add(LL i,LL x){     while(i<=n)     {        c[i]+=x;        i+=lowbit(i);     }}LL tot(int i){    LL sum=0;    while(i>0)    {      sum+=c[i];      i-=lowbit(i);    }    return sum;}LL sum1(int i,int j){   return tot(j)-tot(i-1);}LL find(int x){    if(fa[x]!=x)      fa[x]=find(fa[x]);    return fa[x];}int main(){    //freopen("god.in","r",stdin);    //freopen("god.out","w",stdout);    scanf("%d",&n);    pos(i,1,n+10)      fa[i]=i;    pos(i,1,n)    {      scanf("%lld",&a[i]);      add(i,a[i]);      if(a[i]<=1)        fa[i]=find(find(i)+1);//插入时如果小于等于1,就指向下一位     }    scanf("%d",&m);    pos(i,1,m)    {       int k,l,r;       scanf("%d%d%d",&k,&l,&r);       if(l>r)         swap(l,r);       if(k==0)       {          for(LL j=find(l);j<=r;j=find(j+1))//循环时直接跳着循环           {                            LL tmp=(LL)sqrt(a[j]);              add(j,tmp-a[j]);//相当于把节点修改为更改之后的值               a[j]=tmp;              if(a[j]<=1)                fa[j]=find(j+1);//压缩路径           }       }       else           printf("%lld\n",sum1(l,r));    }    //while(1);    return 0;}


阅读全文
0 0
原创粉丝点击