[BZOJ3038]上帝造題的七分鐘2 樹狀陣列+並查集
阿新 • • 發佈:2018-12-24
考試的時候用了兩個樹狀陣列去優化,暴力修改,樹狀陣列維護修改後區間差值還有最終求和,最後騙了40分。。
這道題有好多種做法,求和好說,最主要的是開方。這道題過的關鍵就是掌握一點:在資料範圍內,最多開方五六次就會變成1,這樣以後再修改就不用修改了。
① 線段樹打標記
② 分塊打標記
③ 樹狀陣列+並查集
因為我考試的時候用的樹狀陣列,所以直接打的第三種,相對來說程式碼量也少一些。
思路:開始時父親都指向自己,如果變成1,就把父親指向下一個位置即可。修改的時候相當於跳著修改。程式碼當中會有註解。
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> using namespace std; #define pos(i,a,b) for(int i=(a);i<=(b);i++) #define pos2(i,a,b) for(int i=(a);i>=(b);i--) #define N 201000 #define LL long long int n,m; LL c[N],cha[N]; LL a[N]; LL fa[N]; LL lowbit(int x) { return x&(-x); } void add(LL i,LL x) { while(i<=n) { c[i]+=x; i+=lowbit(i); } } LL tot(int i) { LL sum=0; while(i>0) { sum+=c[i]; i-=lowbit(i); } return sum; } LL sum1(int i,int j) { return tot(j)-tot(i-1); } LL find(int x) { if(fa[x]!=x) fa[x]=find(fa[x]); return fa[x]; } int main() { //freopen("god.in","r",stdin); //freopen("god.out","w",stdout); scanf("%d",&n); pos(i,1,n+10) fa[i]=i; pos(i,1,n) { scanf("%lld",&a[i]); add(i,a[i]); if(a[i]<=1) fa[i]=find(find(i)+1);//插入時如果小於等於1,就指向下一位 } scanf("%d",&m); pos(i,1,m) { int k,l,r; scanf("%d%d%d",&k,&l,&r); if(l>r) swap(l,r); if(k==0) { for(LL j=find(l);j<=r;j=find(j+1))//迴圈時直接跳著迴圈 { LL tmp=(LL)sqrt(a[j]); add(j,tmp-a[j]);//相當於把節點修改為更改之後的值 a[j]=tmp; if(a[j]<=1) fa[j]=find(j+1);//壓縮路徑 } } else printf("%lld\n",sum1(l,r)); } //while(1); return 0; }