1. 程式人生 > 實用技巧 >分塊演算法初步

分塊演算法初步

題意:

1.給出一個長為的數列,以及個操作,操作涉及區間加法,單點查值。

2.給出一個長為的數列,以及個操作,操作涉及區間加法,詢問區間內小於某個值的元素個數。

3.給出一個長為nn的數列,以及nn個操作,操作涉及區間加法,詢問區間內小於某個值xx的前驅(比其小的最大元素)。

4.給出一個長為nn的數列,以及nn個操作,操作涉及區間加法,區間求和,mod(c+1)。

原題地址:

https://loj.ac/problem/6277

https://loj.ac/problem/6278

https://loj.ac/problem/6279

https://loj.ac/problem/6280

參考部落格:https://www.cnblogs.com/Parsnip/p/10458689.html#

程式碼:

#include<bits/stdc++.h>

#define closeSync ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define multiCase int T;cin>>T;for(int t=1;t<=T;t++)
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define repp(i,a,b) for(int i=a;i<b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define
perr(i,a,b) for(int i=a;i>b;i--) #define pb push_back #define eb push_back #define mst(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; template <typename _Tp> inline _Tp read(_Tp&x){ char c11=getchar(),ob=0;x=0; while(c11^'-'&&!isdigit(c11))c11=getchar();if
(c11=='-')c11=getchar(),ob=1; while(isdigit(c11))x=x*10+c11-'0',c11=getchar();if(ob)x=-x;return x; } const int N=1000010,sqrtN=1000; int n,t,L[sqrtN],R[sqrtN],pos[N];//L[j]、R[j]分別表示第j個塊所管轄區間的左邊界和右邊界,pos[i]表示序列中第i個元素屬於哪個塊 ll a[N],changed[sqrtN],sum[sqrtN];//a陣列用來存序列元素,changed陣列用來表示每個塊中每個元素共同的變化量,sum陣列用來存每個塊中元素的和 vector<int>section[N];//用於管理每個塊中的元素 //輸入元素 inline void input() { scanf("%d",&n); rep(i,1,n)scanf("%d",&a[i]); } //將對應元素存入對應的塊 inline void reset(int x) { section[x].clear(); rep(i,L[x],R[x])section[x].pb(a[i]); sort(section[x].begin(),section[x].end()); } //預處理,分為sqrt(n)或sqrt(n)+1個塊 inline void init() { t=sqrt(n); rep(i,1,t) { L[i]=(i-1)*t+1; R[i]=i*t; } if(R[t]<n)t++,L[t]=R[t-1]+1,R[t]=n; rep(i,1,t) rep(j,L[i],R[i]) pos[j]=i,sum[i]+=a[j]; rep(i,1,t)reset(i); } //區間修改,這裡是整體加data inline void change(int l,int r,ll data) { int p=pos[l],q=pos[r]; if(q==p) { rep(i,l,r)a[i]+=data; sum[p]+=(r-l+1)*data; reset(p); } else { rep(i,p+1,q-1)changed[i]+=data; rep(i,l,R[p])a[i]+=data; sum[p]+=(R[p]-l+1)*data; reset(p); rep(i,L[q],r)a[i]+=data; sum[q]+=(r-L[q]+1)*data; reset(q); } } //用於更新區間內小於某個值 x 的前驅 inline void updata(ll &ans2,ll val,ll limit) { if(val<limit&&val>ans2)ans2=val; } //查詢a[x]的值 inline ll ask1(int x) { return a[x]+changed[pos[x]]; } //查詢區間內小於某個值limit的元素個數 inline int ask2(int l,int r,ll limit) { int p=pos[l],q=pos[r],ans1=0; if(p==q) { rep(i,l,r) if(a[i]+changed[p]<limit)ans1++; } else { rep(i,p+1,q-1)ans1+=lower_bound(section[i].begin(),section[i].end(),limit-changed[i])-section[i].begin(); rep(i,l,R[p])if(a[i]+changed[p]<limit)ans1++; rep(i,L[q],r)if(a[i]+changed[q]<limit)ans1++; } return ans1; } //詢問區間內小於某個值 x 的前驅(比其小的最大元素) inline ll ask3(int l,int r,ll limit) { int p=pos[l],q=pos[r]; ll ans2=-1; if(p==q) { rep(i,l,r) updata(ans2,a[i]+changed[p],limit); } else { rep(i,p+1,q-1) updata(ans2,section[i][lower_bound(section[i].begin(),section[i].end(),limit-changed[i])-section[i].begin()-1]+changed[i],limit); rep(i,l,R[p]) updata(ans2,a[i]+changed[pos[i]],limit); rep(i,L[q],r) updata(ans2,a[i]+changed[pos[i]],limit); } return ans2; } //區間求和取模 inline ll ask4(int l,int r,ll MOD) { int p=pos[l],q=pos[r]; ll ans3=0; if(p==q) { rep(i,l,r)ans3=(ans3+a[i])%MOD; ans3=(ans3+(r-l+1)*changed[p])%MOD; } else { rep(i,p+1,q-1)ans3=(ans3+sum[i]%MOD+(R[i]-L[i]+1)*changed[i])%MOD; rep(i,l,R[p])ans3=(ans3+a[i]+changed[pos[i]])%MOD; rep(i,L[q],r)ans3=(ans3+a[i]+changed[pos[i]])%MOD; } return ans3; } inline void solve() { input(); init(); int opt,l,r;ll c; rep(i,1,n) { scanf("%d%d%d%lld",&opt,&l,&r,&c); if(opt)printf("%lld\n",ask4(l,r,c+1)); else change(l,r,c); } } int main() { solve(); return 0; }