連接:https://ac.nowcoder.com/acm/contest/392/F
來源:牛客網
c++
給定一個長度爲N的序列A,全部元素初值爲0。接下來有M次操做或詢問:華華很快就學會了樹狀數組並經過了這道題。月月也很喜歡樹狀數組,因而給華華出了一道進階題:
操做:輸入格式:1 D K,將 ADAD加上K。
詢問:輸入格式:2 L R,詢問區間和,即∑Ri=LAi∑i=LRAi。
第一行兩個正整數N、M表示序列的長度和操做詢問的總次數。
接下來M行每行三個正整數,表示一個操做或詢問。
對於每一個詢問,輸出一個非負整數表示答案。
1≤N,M≤1051≤N,M≤105,1≤D≤N1≤D≤N,1≤L≤R≤N1≤L≤R≤N,1≤K≤1081≤K≤108
解題思路:首先對於詢問區間和咱們能夠很容易想到須要創建一個樹狀數組,可是對於更新操做卻有點棘手,對於全部知足1≤i≤N且i≡0(modD)的i,將Ai加上K,即須要使下標爲D的倍數的數加上a[i],而這些數基本遍及整個區間[1,n],特別是當D很小時,複雜度越高。
可是咱們能夠發現對於D比較小的狀況,這類狀況也比較有限。好比當D大於sqrt(n)時,咱們所要更新的數字的個數也最多也不超過sqrt(n),而對於這部分數據咱們能夠直接暴力更新就行了,而當D小於等於sqrt(n),咱們能夠用一個標記數組lazy[D]累計起來,標記
lazy[D]+=k,這樣咱們就至關於把區間[1,n]的任意下標x若是x<sqrt(n)把其和其在區間中的倍數當作一個塊,當咱們查詢區間和sum[l,r]時咱們即可以用當前區間和(使用樹狀數組ask(r)-ask(l-1)),加上各個塊在該區間的個數(r-l+1)乘以該塊的標記值(lazy數組值)。
例如:
n=12
下標: 1 2 3 4 5 6 7 8 9 10 11 12
值: 2 1 7 14 6 4 1 7 10 3 7 9
塊數目sqrt(n)=3
把下標爲1的倍數當作是一塊,即下標爲1 2 3 4 5 6 7 8 9 10 11 12的數
把下標爲2的倍數也當作是一塊,即下標爲 2 4 6 8 10 12的這幾個數
把下標爲3的倍數也當作是一塊,即下標爲3 6 9 12的這幾個數
當D的值大於3時,如D=5,k=1,咱們就直接暴力更新各個值更新下標爲5的值和下標爲10的值變成:
下標: 1 2 3 4 5 6 7 8 9 10 11 12
值: 2 1 7 14 7 4 1 7 10 4 7 9
而當D=2,k=1時,咱們選擇不更新數組,而是直接用lazy數組標記,使得lazy[2]+=1,它表示的是2的倍數這一整塊數都須要加上1
而後當咱們詢問區間和時,如詢問區間[3,10]的和時,咱們先用樹狀數組求數組在該區間的和sum=7+14+7+4+1+7+10+4=54;
而後咱們遍歷每個塊,看是否含有標記值,若是有,則加上該塊的標記值*該塊在查詢區間內的我的。
在例子中,lazy[1]爲0(1的倍數並未被標記),表示無標記,則跳過;
當遍歷到2的倍數這一塊時,由於lazy[i]=1含有值,因此咱們須要使sum+=lazy[2]+(10/2-3/2)(其中10/2-3/2表示區間[3,10]中2的倍數的個數)
而當遍歷到3的倍數時,由於lazy[3]=0因此也會跳過,這樣咱們便得出告終果。
代碼:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1e6; int n,m; ll sum[maxn],lazy[maxn]; int lowbit(int x){return x&(-x);} void add(int x,ll val){ for(int i=x;i<=n;i+=lowbit(i)) sum[i]+=val; } ll ask(int x){ ll res=0; for(int i=x;i;i-=lowbit(i)) res+=sum[i]; return res; } int main(){ scanf("%d%d",&n,&m); int block=sqrt(n); while(m--){ int id,x,y; scanf("%d%d%d",&id,&x,&y); if(id==1){ if(x>block) for(int i=x;i<=n;i+=x)add(i,y); //想較大,暴力更新下標爲x的倍數的值 else lazy[x]+=y; //x值較小,用數組標記x的倍數這個塊 } else{ ll ans=ask(y)-ask(x-1); for(int i=1;i<=block;i++) if(lazy[i]) //若是這個塊有標記值,加上該標記值 ans+=(y/i-(x-1)/i)*lazy[i]; printf("%lld\n",ans); } } return 0; }