首页 > 代码库 > Wikioi 2492 树状数组+并查集(单点更新区间查询)

Wikioi 2492 树状数组+并查集(单点更新区间查询)

刚开始做的时候用线段树做的,然后就跳进坑里了……因为要开方,所以区间的值都得全部变,然后想用lazy标记的,但是发现用不了,单点更新这个用不了,然后就不用了,就T了。然后实在不行了,看了别人的题解,原来是用树状数组+并查集的方法,唉……没想到啊!

因为开方之后多次那个数就会变成1了,所以是1的时候开方下去就没用了。树状数组更新的时候就把其更新的差更新即可,太机智了这题……

昨天做了,然后出错找了好久都找不出来,原来是把s[i]写成c[i]了,然后答案一直错,晕……

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include <set>
#define PI acos(-1.0)
#define mem(a,b) memset(a,b,sizeof(a))
#define sca(a) scanf("%d",&a)
#define sc(a,b) scanf("%d%d",&a,&b)
#define pri(a) printf("%d\n",a)
#define lson i<<1,l,mid
#define rson i<<1|1,mid+1,r
#define MM 2000004
#define MN 1008
#define INF 2000000000
#define eps 1e-8
using namespace std;
typedef long long ll;
typedef unsigned long long ULL;
ll c[MM],s[MM];
int f[MM],n;
void update(int x,ll num)
{
    for(int i=x;i<=n;i+=i&(-i))
        c[i]+=num;
}
ll sum(int x)
{
    ll ans=0;
    for(int i=x;i>=1;i-=i&(-i))
        ans+=c[i];
    return ans;
}
int find(int x)
{
    return f[x]==x?x:f[x]=find(f[x]);
}
void solve(int a,int b)
{
    for(int i=find(a);i<=b;i=find(i+1))
    {
        ll k=(ll)sqrt(s[i]);
        update(i,k-s[i]);
        s[i]=k;
        if(k==1) f[i]=i+1;
    }
}
int main()
{
    int q,i,a,b,k;
    sca(n);
    for(i=0;i<=n+10;i++) f[i]=i;
    for(i=1;i<=n;i++) scanf("%lld",&s[i]),update(i,s[i]);
    sca(q);
    while(q--)
    {
        scanf("%d%d%d",&k,&a,&b);
        if(a>b) swap(a,b);
        if(!k) solve(a,b);
        else printf("%lld\n",sum(b)-sum(a-1));
    }
    return 0;
}