bzoj 3211:花神游历各国/3038 上帝造题的七分钟2(luogu 4145)

本文介绍了一种结合并查集与树状数组/线段树的算法,用于解决区间开方和区间求和问题。通过并查集优化路径压缩,实现高效区间操作,避免了暴力算法的高时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

算法:并查集+树状数组/分块/线段树 

难度:(NOIP+)

简述题目:区间开方,区间求和
   区间求和很容易想到树状数组/线段树,可是区间开方怎么搞呢?暴力O(n*n),TLE到飞
   我们可以发现,一个10^12的数,最多开6次方(向下取整)可以变成1,变成1之后,无论开多少次方都是1,所以就可以跳过这个数,这个可以用并查集搞,开始时父亲都指向自己,如果变成1,就把父亲指向下一个位置即可。修改的时候相当于跳着修改!

代码如下:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <queue>
#include <cmath>
#define ll long long
#define N 200015
using namespace std;
ll a[N],c[N];
int fa[N];
int findf(int x)
{
    if(x==fa[x]) return x;
    return fa[x]=findf(fa[x]);
}
int lowbit(int x)
{
    return x & (-x);
}
int l,r,k,n;
void add(int x,ll val)
{
    while(x<=n)
    {
        c[x]+=val;
        x+=lowbit(x);
    }
}
ll query(int x)
{
    ll ret=0;
    while(x)
    {
        ret+=c[x];
        x-=lowbit(x);
    }
    return ret;
}
int main()
{
    scanf("%d",&n);
    for(int i = 1;i <= n+5/*防止findf(n+1)  RE*/;i++)
    {
        fa[i]=i;
    }
    for(int i = 1;i <= n;i++)
    {
        scanf("%lld",&a[i]);
        add(i,a[i]);//忘记add,我在玩什么? 
        if(a[i]<=1)
        {
            fa[i]=findf(findf(i)+1);
        }
    }
    int m;
    scanf("%d",&m);
    while(m--)
    {
        scanf("%d%d%d",&k,&l,&r);
        if(l>r) swap(l,r);
        if(k==2)
        {
            for(int i = findf(l);i<=r;i=findf(i+1/*指向下一位*/))
            {
                ll tmp=sqrt(a[i]);
                add(i,tmp-a[i]);//(sqrt(x)-x)+x=sqrt(x)
                a[i]=tmp;
                if(a[i]<=1)
                {
                    fa[i]=findf(i+1);//压缩路径 
                }    
            }
        }else
        {
            printf("%lld\n",query(r)-query(l-1));
        }
    }
    return 0 ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值