原題位址:
題意:給出乙個長為 n 的數列 a,以及 n 個操作,操作涉及區間開方,區間求和。
思路:對於整塊的開方操作不像加法一樣可以疊加,開方必須要對每乙個數進行操作,這樣才能進行區間加法運算,但是這樣的話就等於暴力計算每乙個數字,複雜度是n2
n
2,這樣無疑是超時的。於是我們考慮開方的性質,可以發現對於乙個int型別的整數,最多經過5次的連續開方之後就會就成1,然後1再開方只能是1.因此我們可以記錄每一塊的情況,如果當前塊的數字已經全是1或0的話,那麼下次這個塊的開方就不用考慮了。這樣的話,複雜就大大降低了,因為計算的時候可以忽略好多塊。
同樣附上hzwer的解法:
稍作思考可以發現,開方操作比較棘手,主要是對於整塊開方時,必須要知道每乙個元素,才能知道他們開方後的和,也就是說,難以快速對乙個塊資訊進行更新。
看來我們要另闢蹊徑。不難發現,這題的修改就只有下取整開方,而乙個數經過幾次開方之後,它的值就會變成 0 或者 1。如果每次區間開方只不涉及完整的塊,意味著不超過2√n個元素,直接暴力即可。如果涉及了一些完整的塊,這些塊經過幾次操作以後就會都變成 0 / 1,於是我們採取一種分塊優化的暴力做法,只要每個整塊暴力開方後,記錄一下元素是否都變成了 0 / 1,區間修改時跳過那些全為 0 / 1 的塊即可。這樣每個元素至多被開方不超過4次,顯然複雜度沒有問題。
#include
using
namespace
std;
typedef
long
long ll;
const
int maxn = 5e4 + 5;
int n, block, belog[maxn], opt, l, r, c;
int a[maxn];
ll add[maxn], sum[maxn];
int flag[maxn];
inline
int read()
void updata(int l, int r, int c)
if(t1 != t2)
for(int i = t1 + 1; i <= t2 - 1; i++) }}
}}void query(int l, int r, int c)
if(t1 != t2)
for(int i = t1 + 1; i <= t2 - 1; i++)
}printf("%lld\n", ret);
}int main()
while(n--)
return
0;}
Loj 6281 數列分塊入門 5
思路 因為是向下取整開方,那麼其實每個數最多被開個個幾次就會變成0或者1了,更新的時候我們可以將中間的塊標記下是否全部變成了0或者1,如果全變了就不處理 實現 includeusing namespace std define ll long long const int m 1e5 10 vect...
LOJ 6281 數列分塊入門 5
記憶體限制 256 mib時間限制 500 ms標準輸入輸出 題目型別 傳統評測方式 文字比較 上傳者 hzwer 提交提交記錄 統計討論 1測試資料 題目描述 給出乙個長為 nnn 的數列,以及 nnn 個操作,操作涉及區間開方,區間求和。輸入格式 第一行輸入乙個數字 nnn。第二行輸入 nnn ...
6281 數列分塊入門 5
題目鏈結 用check來判斷整個塊是0是1,如果是0或者是1,就不用再對這個塊裡的元素開方了。對乙個數不斷開方一定會縮小到0或者1。第一行輸入乙個數字 n。第二行輸入 n 個數字,第 i 個數字為 ai,以空格隔開。接下來輸入 n 行詢問,每行輸入四個數字 opt l r c,以空格隔開。若 opt...