2018.09.15【codevs2492】上帝造题的七分钟2(并查集)(树状数组)(线段树)

版权声明:转载请声明出处,谢谢配合。 https://blog.csdn.net/zxyoi_dreamer/article/details/82713740

传送门


解析:

这里的并查集并不能用集合合并的思想来考虑,

这里并查集有点像图论中的 D A G 里的边,记录的是当前节点的下一个(包括当前)非1节点的位置。

显然,对1开方对答案是没有任何影响的。

那么我们就只对非1的开方。

那么我们就只遍历非1的节点。

修改和查询用树状数组就好了。

一个数最多被开方 l o g   l o g   a r r i 次,所以复杂度是均摊的,套上树状数组的 O ( l o g n ) 也能过。
不知道那些说并查集复杂度有问题的人是什么心态。。。这里明明是均摊的复杂度。。。

线段树做法也很简单啊,记录这个区间还是否需要修改,不用修改就直接 r e t u r n ,仍然是均摊复杂度。


代码(树状数组+并查集):

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline
ll getint(){
    re ll num=0;
    re char c;
    while(!isdigit(c=gc()));
    while(isdigit(c))num=(num<<1)+(num<<3)+(c^48),c=gc();
    return num;
}

inline
void outint(ll a){
    static char ch[23];
    if(a==0)pc('0');
    while(a)ch[++ch[0]]=(a-a/10*10)^48,a/=10;
    while(ch[0])pc(ch[ch[0]--]);
}

inline
int lowbit(int k){return k&(-k);}

int n,m;
ll a[100002];

inline
void add(int pos,ll d){
    while(pos<=n){
        a[pos]+=d;
        pos+=lowbit(pos);
    }
}

inline
ll query(int pos){
    ll res=0;
    while(pos){
        res+=a[pos];
        pos-=lowbit(pos);
    }
    return res;
}

int fa[100002];
int getfa(int x){
    return x==fa[x]?x:fa[x]=getfa(fa[x]);
}

ll arr[100002];

int main(){
    n=getint();
    for(int re i=1;i<=n;++i){
        arr[i]=getint();
        add(i,arr[i]);
        fa[i]=i;
    }
    fa[n+1]=n+1;//哨兵节点
    m=getint();
    while(m--){
        int op=getint();
        int l=getint(),r=getint();
        if(l>r)swap(l,r);
        switch(op){
            case 1:{
                outint(query(r)-query(l-1));pc('\n');
                break;
            }
            case 0:{
                for(int re i=l;i<=r;i=getfa(i+1)){
                    ll sq=sqrt(arr[i]);
                    add(i,sq-arr[i]);
                    arr[i]=sq;
                    if(arr[i]==1)fa[i]=i+1;
                }
                break;
            }
        }
    }
    return 0;
}

代码(线段树):

扫描二维码关注公众号,回复: 3291873 查看本文章
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline
ll getint(){
    re ll num=0;
    re char c;
    while(!isdigit(c=gc()));
    while(isdigit(c))num=(num<<1)+(num<<3)+(c^48),c=gc();
    return num;
}

inline
void outint(ll a){
    static char ch[23];
    if(a==0)pc('0');
    while(a)ch[++ch[0]]=(a-a/10*10)^48,a/=10;
    while(ch[0])pc(ch[ch[0]--]);
}

ll sum[400005];
int n,m;
ll a[100002];
bool sam1[400005];

inline
void pushup(int k){
    sum[k]=sum[k<<1]+sum[k<<1|1];
    sam1[k]=sam1[k<<1]&&sam1[k<<1|1];
}

inline
void build(int k,int l,int r){
    if(l==r){
        sum[k]=a[l];
        sam1[k]=(a[l]==1);
        return ;
    }
    int mid=(l+r)>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
    pushup(k);
}

inline
void modify(int k,int l,int r,cs int &ql,cs int &qr){
    if(sam1[k])return;
    if(l==r){
        sum[k]=floor(sqrt(sum[k]));
        sam1[k]=(sum[k]==1);
        return ;
    }
    int mid=(l+r)>>1;
    if(ql<=mid)modify(k<<1,l,mid,ql,qr);
    if(qr>mid)modify(k<<1|1,mid+1,r,ql,qr);
    pushup(k);
}

inline
ll query(int k,int l,int r,cs int &ql,cs int &qr){
    if(ql<=l&&r<=qr)return sum[k];
    int mid=(l+r)>>1;
    if(mid<ql)return query(k<<1|1,mid+1,r,ql,qr);
    if(mid>=qr)return query(k<<1,l,mid,ql,qr);
    return query(k<<1,l,mid,ql,qr)+query(k<<1|1,mid+1,r,ql,qr);
}

signed main(){
    n=getint();
    for(int re i=1;i<=n;++i)a[i]=getint();
    build(1,1,n);
    m=getint();
    while(m--){
        int op=getint();
        int l=getint(),r=getint();
        if(l>r)swap(l,r);
        switch(op){
            case 1:{
                outint(query(1,1,n,l,r));pc('\n');
                break;
            }
            case 0:{
                modify(1,1,n,l,r);
                break;
            }
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/zxyoi_dreamer/article/details/82713740
今日推荐