zoukankan      html  css  js  c++  java
  • 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;
    }
    

      

  • 相关阅读:
    HTTP协议
    UI- 不易记知识点汇总
    UI- 五种手势识别总结
    idea整合 springboot jsp mybatis
    xml和map互转工具类
    ajax请求案例
    java加密工具类,可设置对应的加解密key
    ajax请求正常,返回json格式,后台没问题,浏览器500
    通过工具SecureCRTPortable将项目部署到服务器上
    修改idea自动生成在C盘的文件路径,以免电脑越用越卡
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/BZOJ3211.html
Copyright © 2011-2022 走看看