上週開始講分塊,然而和我想的不同,你們都聽得很懵13 --> 以致於我對於本身對於分塊的理解產生了懷疑。ios
結果天然是寫一篇blog自我檢驗一下了啊。算法
那麼切入正題。數組
簡意
將一段暴力很費事的區間拆分紅數個小區間分開求解,並採起預處理的方式下降複雜度。spa
這種算法真的是很好的啊。code
尤爲是對於咱們這種懶惰oier來講,簡直就是福音啊。blog
實現方法
將一段長序列分紅相同的塊大小,事先將整塊的數據完成整理。而後進行整散拆分,整塊直接使用,散塊暴力。get
那麼這又有什麼沒法理解的地方呢?我真的很不理解QAQstring
那麼接下來咱們採起例題的方式來更具象的理解一下吧。it
題目描述:
現有N(2 ≤ N ≤ 100000)盞燈排成一排,從左到右依次編號爲:1,2,......,N。io
而後依次執行M(1 ≤ M ≤ 100000)項操做,操做分爲兩種:
第一種操做指定一個區間[a, b],而後改變編號在這個區間內的燈的狀態(把開着的燈關上,關着的燈打開)。
第二種操做是指定一個區間[a, b],要求你輸出這個區間內有多少盞燈是打開的。燈在初始時都是關着的。
輸入:
第一行有兩個整數N和M,分別表示燈的數目和操做的數目。
接下來有M行,每行有三個整數,依次爲:c, a, b。其中c表示操做的種類:
當c的值爲0時,表示是第一種操做。
當c的值爲1時表示是第二種操做。
a和b則分別表示了操做區間的左右邊界(1 ≤ a ≤ b ≤ N)。
輸出:
每當遇到第二種操做時,輸出一行,包含一個整數:此時在查詢的區間中打開的燈的數目。
樣例輸入:
4 5 0 1 2 0 2 4 1 2 3 0 2 4 1 1 4樣例輸出:
1 2
這種東西真的很顯然了啊,我都說了是分塊的裸題了啊QAQ
咱們在修改的時候對於散塊直接暴力修改,整塊把用來標記的數組異或一下 1 就好了,修改次數爲偶數時燈天然就不變了啊。
但這樣的話很差查整塊,因此須要維護一個ans[i],用於保存塊內目前開着的燈的數量。
散塊修改的時候好維護,而整塊修改的時候把用塊的大小減去ans維護就好了。
那麼查詢的時候天然也很顯然了啊,散塊暴力,整塊ans。
風格較醜,請見諒哈。
#include<iostream> #include<cstring> #include<cmath> #include<cstdio> #include<algorithm> #include<queue> #define rint register int #define maxn 200010 using namespace std; inline int read() { int s=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){ch=getchar();if(ch=='-')f=-1;} while(ch>='0'&&ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar(); return s*f; } int n,m,blo,o; int bl[maxn],v[maxn],t[maxn],atag[maxn],sum[maxn]; int L[maxn],R[maxn]; inline void change(int l,int r) { if(bl[l]==bl[r]) { for(rint i=l;i<=r;++i) { if(v[i]) { v[i]=0; --sum[bl[l]]; } else { v[i]=1; ++sum[bl[l]]; } } return; } for(rint i=l;i<=R[bl[l]];++i) { if(v[i]) { v[i]=0; --sum[bl[l]]; } else { v[i]=1; ++sum[bl[l]]; } } for(rint i=L[bl[r]];i<=r;++i) { if(v[i]) { v[i]=0; --sum[bl[r]]; } else { v[i]=1; ++sum[bl[r]]; } } for(rint i=bl[l]+1;i<=bl[r]-1;++i) atag[i]=1-atag[i]; } inline int query(int l,int r) { int ans=0; if(bl[l]==bl[r]) { for(rint i=l;i<=r;++i) if(v[i]^atag[bl[l]]) ++ans; return ans; } for(int i=l;i<=R[bl[l]];++i) if(v[i]^atag[bl[l]]) ++ans; for(int i=L[bl[r]];i<=r;++i) if(v[i]^atag[bl[r]]) ++ans; for(int i=bl[l]+1;i<=bl[r]-1;++i) { if(atag[i]) ans+=(R[i]-L[i]+1)-sum[i]; else ans+=sum[i]; } return ans; } int main() { n=read(); m=read(); blo=sqrt(n); n%blo?(o=n/blo+1):(o=n/blo); for(rint i=1;i<=n;++i) { v[i]=0; bl[i]=(i-1)/blo+1; if(v[i]) ++sum[bl[i]]; } for(rint i=1;i<=o;++i) { L[i]=(i-1)*blo+1; R[i]=i*blo; } while(m--) { int p=read(); int l=read(); int r=read(); if(p==0) change(l,r); if(p==1) cout<<query(l,r)<<endl; } return 0; }
如此這題就完了耶~
做爲一個懶 的打題的人怎麼能放過雙倍經驗呢??????
P2846 [USACO08NOV]光開關Light Switching
祝你們RP++!!