• BZOJ3211 花神游历各国 并查集 树状数组


    欢迎访问~原文出处——博客园-zhouzhendong

    去博客园看该题解


    题目传送门 - BZOJ3211


    题意概括

      有n个数形成一个序列。

      m次操作。

      有两种,分别是:

    1. 区间开根(取整)

    2. 区间求和


    题解

      这题做法大概我知道的有两种,一种是线段树,一种是并查集+树状数组。

      两者都基于一个事实:任何一个数被开根很少的次数就变成1了,然后不变了。所以我们可以暴力解决这个开根的问题。

      线段树就打一下lazy标记就可以了。

      这里主要讲并查集和树状数组怎么做。

      树状数组维护前缀和。

      并查集的作用是跳过那些1的点。

      如果第i个数变成了1,那么我们就让它认第i+1个点为爸爸。

      那么对于第i个数,我们只需要求一下祖先就可以找到从第i个数开始的第一个会变的数了。

      复杂度很小的。。

      但是一开始Tle了……

      注意:有一个非负整数叫做0。

      0的处理和1等价。


    代码

    #include <cstring>
    #include <algorithm>
    #include <cstdio>
    #include <cmath>
    #include <cstdlib>
    using namespace std;
    typedef long long LL;
    const int N=100005;
    int n,m,fa[N],v[N];
    LL tree[N];
    int lowbit(int x){
    	return x&-x;
    }
    void add(int x,int d){
    	for (;x<=n;x+=lowbit(x))
    		tree[x]+=d;
    }
    LL sum(int x){
    	LL ans=0;
    	for (;x>0;x-=lowbit(x))
    		ans+=tree[x];
    	return ans;
    }
    int getf(int k){
    	return fa[k]==k?k:fa[k]=getf(fa[k]);
    }
    int main(){
    	scanf("%d",&n);
    	memset(tree,0,sizeof tree);
    	for (int i=1;i<=n;i++){
    		scanf("%d",&v[i]);
    		add(i,v[i]);
    		fa[i]=i;
    	}
    	fa[n+1]=n+1;
    	scanf("%d",&m);
    	while (m--){
    		int op,a,b;
    		scanf("%d%d%d",&op,&a,&b);
    		if (op==1)
    			printf("%lld
    ",sum(b)-sum(a-1));
    		else {
    			for (int i=getf(a);i<=b;i=getf(i+1)){
    				int v_=floor(sqrt(v[i]));
    				add(i,v_-v[i]);
    				v[i]=v_;
    				if (v[i]<=1)
    					fa[i]=getf(i+1);
    			}
    		}
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    checkbox radio select 选中总结
    vue-cli3总结
    数组总结
    Object 总结
    ajax总结
    canvas
    移动端事件
    微服务架构 SpringBoot(一)
    spring+activemq实战之配置监听多队列实现不同队列消息消费
    Integer 数值比较
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/BZOJ3211.html
Copyright © 2020-2023  润新知