1. 程式人生 > >洛谷P4145 上帝造題的七分鐘2/花神遊歷各國 [樹狀數組,並查集]

洛谷P4145 上帝造題的七分鐘2/花神遊歷各國 [樹狀數組,並查集]

typedef -html org 而且 open als noip 直接 update

  題目傳送門

  

題目背景

XLk覺得《上帝造題的七分鐘》不太過癮,於是有了第二部。

題目描述

"第一分鐘,X說,要有數列,於是便給定了一個正整數數列。

第二分鐘,L說,要能修改,於是便有了對一段數中每個數都開平方(下取整)的操作。

第三分鐘,k說,要能查詢,於是便有了求一段數的和的操作。

第四分鐘,彩虹喵說,要是noip難度,於是便有了數據範圍。

第五分鐘,詩人說,要有韻律,於是便有了時間限制和內存限制。

第六分鐘,和雪說,要省點事,於是便有了保證運算過程中及最終結果均不超過64位有符號整數類型的表示範圍的限制。

第七分鐘,這道題終於造完了,然而,造題的神牛們再也不想寫這道題的程序了。"

——《上帝造題的七分鐘·第二部》

所以這個神聖的任務就交給你了。

輸入輸出格式

輸入格式:

第一行一個整數 nn ,代表數列中數的個數。

第二行 nn 個正整數,表示初始狀態下數列中的數。

第三行一個整數 mm ,表示有 mm 次操作。

接下來 mm 行每行三個整數k,l,r

  • k=0表示給 [l,r][l,r] 中的每個數開平方(下取整)
  • k=1表示詢問 [l,r][l,r] 中各個數的和。

數據中有可能 l>rl>r ,所以遇到這種情況請交換l和r。

輸出格式:

對於詢問操作,每行輸出一個回答。

輸入輸出樣例

輸入樣例#1: 復制
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
輸出樣例#1: 復制
19
7
6

說明

對於30%的數據, 1\le n,m\le 10001n,m1000 ,數列中的數不超過 3276732767 。

對於100%的數據, 1 \le n,m \le 1000001n,m100000 , 1 \le l,r \le n1l,rn ,數列中的數大於 00 ,且不超過 10^{12}1012 。

註意l有可能大於r,遇到這種情況請交換l,r。


  分析:首先,要求進行區間修改和區間查詢,很明顯的線段樹啊。這題用線段樹確實也可以做,但是開方是不支持區間操作的,所以只能單點修改,但是會發現單點修改最多只能進行6次,因為log(log(1e12))約為6,向下取整就得到,每一個數只能操作六次,那麽就打標記,直接暴力單點修改就可以A了。

  但是這題還可以用樹狀數組套並查集做,而且更優。因為樹狀數組是用於保存前綴和的,那麽每一次修改只要修改前綴和即可,每次的操作復雜度為O(logn),查詢也一樣。那麽修改操作和線段樹也差不多。每次單點修改,最多只有6次,但是這樣復雜度還是O(n^2),那麽並查集的作用就顯現出來了。每次修改過以後,如果該點的值已經<=1了,那麽就將該點的fa[i]修改為i+1,修改的for循環就可以這樣寫:

  for(int i=find(l);i<=r;i=find(i+1))

  就可以直接跳過不需要修改的點,優化復雜度了,平攤下來復雜度大約也就是樹狀數組的復雜度O(nlogn)。

  Code:

 1 #include<bits/stdc++.h>
 2 #define Fi(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
 3 using namespace std;
 4 typedef long long ll;
 5 const int N=1e5+7;
 6 ll a[N],c[N];int n,m,fa[N];
 7 inline ll read()
 8 {
 9   char ch=getchar();ll num=0;bool flag=false;
10   while(ch<0||ch>9){if(ch==-)flag=true;ch=getchar();}
11   while(ch>=0&&ch<=9){num=num*10+ch-0;ch=getchar();}
12   return flag?-num:num;
13 }
14 inline int find(int x){return (fa[x]==x||!fa[x])?fa[x]=x:fa[x]=find(fa[x]);}
15 inline void update(int x,ll y){for(;x<=n;x+=x&-x)c[x]+=y;}
16 inline ll get(int x){ll ret=0;for(;x;x-=x&-x)ret+=c[x];return ret;}
17 inline void change(int l,int r)
18 {
19   for(int i=find(l);i<=r;i=find(i+1)){
20     ll temp=(ll)sqrt(a[i]);update(i,temp-a[i]);
21     a[i]=temp;if(a[i]<=1)fa[i]=find(i+1);}
22 }
23 int main()
24 {
25   n=read();Fi(i,1,n){a[i]=read();update(i,a[i]);fa[i]=i;if(a[i]<=1)fa[i]=i+1;}
26   m=read();ll x,y,z;Fi(i,1,m){x=read();y=read();z=read();if(y>z)swap(y,z);
27     if(x==0)change(y,z);else printf("%lld\n",get(z)-get(y-1));}return 0;
28 }

洛谷P4145 上帝造題的七分鐘2/花神遊歷各國 [樹狀數組,並查集]