1. 程式人生 > >Luogu T14448 區間開方

Luogu T14448 區間開方

平方根 pri its clu 輸入格式 進行 org 題解 無需

題面版權來自Shlw。題目鏈接

題目背景

題目描述

給定一個數列,元素均為正整數,對其以下兩種操作:

1.將某區間每一個數變為其算術平方根(取整)

2.求出某區間內所有數的最大值

輸入輸出格式

輸入格式:

第一行包含兩個整數n,m,分別表示該數列數字的個數和操作的總個數。第二行包含n個用空格分隔的整數,表示給定的數列。接下來m行,每行包含3個整數,表示一個操作,具體如下:

操作1:1 x y 含義:將區間[x,y]內每個數進行開方。

操作2:2 x y 含義:輸出區間[x,y]內所有數的最大值。

輸出格式:

輸出包含若幹行整數,即為所有操作2的結果。

輸入輸出樣例

輸入樣例#1:
9 7
71 25 69 41 75 91 12 76 24 
1 1 2
1 1 2
1 1 1
1 3 7
2 9 9
1 7 7
2 1 2
輸出樣例#1:
24
2

說明

對於20%的數據,n,m<=1000。

對於60%的數據,n,m<=10000。

對於100%的數據,n,m<=200000。

數據全部隨機。

題解:

20分做法:

直接暴力。復雜度O(n*m)。

60分做法:

我不知道。網上有關於線段樹處理區間開方區間求和的題,只是自己不會寫,不知道能不能遷移到區間最值上。(其實是為了看起來有部分分才設置60%數據的)

100分做法:

註意到數據是較為隨機的,也就是說m次操作中差不多有m/2次開方操作,區間隨機時每個元素被開方m/4次。然而一個9位數開方幾十次之後就會變成1,以後的所有開方操作都是毫無意義的。考慮到元素最小值為1,所以在查詢最大值時也沒有必要將1考慮進去。總而言之,1是“無需處理的元素”,然而在m次操作後期序列中會出現很長很長的一段全為1的元素,可以在區間操作時考慮跳過。

關於跳過一些未來可能會合並但一定不會分裂的區間可以使用並查集。

與之相似的一道題: CodeVS 4874 染色 題目鏈接

設fa[i]為“i以及i以後第一個不為1的數的編號”。換句話說,若a[i]不為1,則fa[i]=i;若a[i]為1,則fa[i]為i後第一個不為1的數的編號。發現fa可以進行路徑壓縮,在處理開方和求max時,從find(l)開始,逐次開方或求max,處理後若當前元素為1,將當前元素與下一個進行合並,否則開始處理下一個元素。

復雜度:O(跑得過)(手動滑稽)

代碼:

技術分享
 1 #include<bits/stdc++.h>
 2 using namespace std;
3 const int maxn=2e5+10; 4 int a[maxn],fa[maxn]; 5 int n,m; 6 int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);} 7 void change(int l,int r) 8 { 9 int i,j; 10 i=l; 11 do 12 { 13 i=find(i); 14 if(i>r){break;} 15 a[i]=(int)(sqrt(a[i])); 16 if(a[i]==1){fa[i]=find(i+1);} 17 else{i++;} 18 }while(i<=r); 19 //for(i=1;i<=n;i++){cout<<a[i]<<" ";}cout<<endl; 20 } 21 void query(int l,int r) 22 { 23 int i=l,j,ans=1; 24 do 25 { 26 i=find(i); 27 if(i>r){break;} 28 ans=max(ans,a[i]); 29 if(a[i]==1){fa[i]=find(i+1);} 30 else{i++;} 31 }while(i<=r); 32 printf("%d\n",ans); 33 } 34 int main() 35 { 36 int i,j,flag,l,r; 37 //freopen("data.in","r",stdin); 38 //freopen("test.out","w",stdout); 39 cin>>n>>m; 40 for(i=1;i<=n;i++){scanf("%d",&a[i]);} 41 for(i=1;i<=n+1;i++){fa[i]=i;} 42 for(i=1;i<=m;i++) 43 { 44 scanf("%d%d%d",&flag,&l,&r); 45 if(flag==1){change(l,r);} 46 else{query(l,r);} 47 } 48 return 0; 49 }
View Code

Luogu T14448 區間開方