1. 程式人生 > >BZOJ_3038_上帝造題的七分鐘2_線段樹

BZOJ_3038_上帝造題的七分鐘2_線段樹

表示範圍 pan clas oid 難度 fine class body bzoj

BZOJ_3038_上帝造題的七分鐘2_線段樹

題意:

XLk覺得《上帝造題的七分鐘》不太過癮,於是有了第二部。
"第一分鐘,X說,要有數列,於是便給定了一個正整數數列。
第二分鐘,L說,要能修改,於是便有了對一段數中每個數都開平方(下取整)的操作。
第三分鐘,k說,要能查詢,於是便有了求一段數的和的操作。
第四分鐘,彩虹喵說,要是noip難度,於是便有了數據範圍。
第五分鐘,詩人說,要有韻律,於是便有了時間限制和內存限制。
第六分鐘,和雪說,要省點事,於是便有了保證運算過程中及最終結果均不超過64位有符號整數類型的表示範圍的限制。
第七分鐘,這道題終於造完了,然而,造題的神牛們再也不想寫這道題的程序了。"
——《上帝造題的七分鐘·第二部》
所以這個神聖的任務就交給你了。

分析:

在2^64-1以內每個數最多被開方6次就會變成1,或者這個數一開始就是零。

用線段樹維護區間和,再記錄下區間裏的數有沒有變成1。

代碼:

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
using namespace std;
#define N 262145
#define LL long long
#define BUG puts("Fuck")
int ls[N],rs[N],n,m,cnt;
LL t[N],len[N];
void bt(int l,int r,int &p){
    if(!p)p=++cnt;
    if(l==r){
        scanf("%lld",&t[p]);
        if(t[p])len[p]=1;return ;
    }
    int mid=l+r>>1;
    bt(l,mid,ls[p]);bt(mid+1,r,rs[p]);
    t[p]=t[ls[p]]+t[rs[p]];
    len[p]=len[ls[p]]+len[rs[p]];
}
void up(int l,int r,int x,int y,int p){
    if(t[p]==len[p])return ;
    if(l==r){t[p]=(LL)sqrt(t[p]);return ;}
    int mid=l+r>>1;
    if(x<=mid)up(l,mid,x,y,ls[p]);
    if(y>mid)up(mid+1,r,x,y,rs[p]);
    t[p]=t[ls[p]]+t[rs[p]];
}
LL query(int l,int r,int x,int y,int p){
    if(x<=l&&r<=y)return t[p];
    int mid=l+r>>1;
    LL re=0;
    if(x<=mid)re+=query(l,mid,x,y,ls[p]);
    if(y>mid)re+=query(mid+1,r,x,y,rs[p]);
    return re;
}
int main(){
    scanf("%d",&n);
    int p=0;
    bt(1,n,p);
    scanf("%d",&m);
    int opt,x,y;
    while(m--){
        scanf("%d%d%d",&opt,&x,&y);
        if(x>y)swap(x,y);
        if(opt==0){
            p=1;
            up(1,n,x,y,p);
        }else{
            p=1;
            printf("%lld\n",query(1,n,x,y,p));
        }
    }
}
/*
10
1 2 3 4 5 6 7 8 9 10
5
0 1 10
1 1 10
1 1 5
0 5 8
1 4 8
*/

BZOJ_3038_上帝造題的七分鐘2_線段樹