從疫情中的體溫測量到分塊思想的運用

Once upon a time,COVID-19席捲全球,Chinese Government要求學校復課時必須測量學生體溫
YC中學有幾萬名同窗,要找到發燒的同窗進行隔離 若是要讓一位老師完成全部測溫任務,那這將是一個大工程,效率會很低(左圖)
因此將學校全部同窗分紅班級進行,而後彙總,效率會更高(右圖)c++

在這裏插入圖片描述

剛剛中咱們說的:算法

將學校全部同窗分紅班級進行,而後彙總數組

這就是一種分塊
那問題來了,什麼是分塊呢?
其實經過剛剛的情景,你已經領悟到了分塊的本質:函數

將一個總體劃分爲若干個小塊,進行處理優化

算法中,與之對應的就是:spa

總體 小塊
學校 班級
數組 若干元素

那麼,分塊究竟是怎麼一種思想呢?
整塊維護,殘塊查找code

仍是以測量體溫舉例:blog

如今YC中學要查找體溫在36℃~37.5℃區間內的同窗
怎麼作呢?
不可能又去挨個同窗去統計、去數吧
那就作一張大表吧,在以前測溫的時候就把34 ~ 35℃、35 ~36℃、36 ~ 37℃、37 ~ 38℃、38 ~ 39℃......的同窗分別列出來,數量分別加出來
而後36 ~ 37℃能夠就直接在表裏查出人數
那37 ~ 37.5℃怎麼辦呢?
表內並無37 ~ 37.5℃的這樣0.5大小的區間啊
那就在37 ~ 38℃這個區間去找唄
方法能夠直接暴力遍歷,也能夠二分查找等等圖片

剛剛解決的問題就是一個典型的分塊
像34 ~ 35℃、35 ~36℃、36 ~ 37℃、37 ~ 38℃、38 ~ 39℃這種列在表上給出的就是整塊
37 ~ 37.5℃這種表上沒有,包含在一個其餘整塊中的但又不足一個整塊的就叫作殘塊ip

不難發現,其實分塊這個思想是一種暴力,一種優化的暴力,但每每也頗有效
Such as 線段樹過於臃腫,代碼冗長,大材小用;而直接暴力就會TLE,不能知足數據大小
這就很適合分塊了
那麼咱們具體怎麼作呢?

咱們先要求得應該分爲多少個區塊嘛,而後求得每一個區塊應該包含多少個元素
而後在輸入時分塊
要使狀況最優,那麼區塊既不能太少也不能太多
太少,整塊的數量會太少,花費大量的時間處理殘塊
太多,區塊的長度會過短,失去總體處理區塊的意義
因此,咱們取塊數爲根號n
而開平方開不盡的n,咱們一般在最後接一個不足整塊元素的假整塊(能夠看作整塊)
這樣在最壞狀況下
咱們要處理接近根號n整塊,還要對長度爲 2倍根號n 的殘塊最後單獨處理

cin>>n;
	blo=sqrt(n);//sqrt()開平方函數
	for(int i=1;i<=n;i++){
		cin>>a[i];//儲存元素a[i]
		pos[i]=(i-1)/blo+1;//pos[i]爲記錄元素a[i]屬於第幾個整塊
		m[pos[i]]=max(a[i],m[pos[i]]);//尋找第pos[i]個整塊的最大值存入m[pos[i]]
	}

咱們先統計左右殘塊,而後再統計整塊

cin>>q;
	int l,r;
	while(q--){
		cin>>l>>r;
		l++;
		r++;
		int ans=0;
		for(int i=l;i<=min(r, pos[l]*blo);i++){//統計左殘缺塊 
			ans=max(ans,a[i]);
		} 
		if(pos[l]!=pos[r]){//存在右殘缺塊 
			for(int i=(pos[r]-1)*blo+1;i<=r;i++){//統計右殘缺塊 
				ans=max(ans,a[i]);
			}
		} 
		for( int i=pos[l]+1;i<=pos[r]-1;i++){//統計中間整塊 
			ans=max(ans,m[i]);
		}	
		cout<<ans<<endl;
	}

分塊入門之求最大值

先看一個例題

分塊入門之求最大值
Input
第一行給出一個數字N,接下來N+1行,每行給出一個數字Ai,(1<=i<=N<=1E5)
接來給出一個數字Q(Q<=7000),表明有Q個詢問
每組詢問格式爲a,b即詢問從輸入的第a個數到第b個數,其中的最大值是多少
Output
如題所述
Sample Input
10 0 1 2 3 2 3 4 3 2 1 0 5 0 10 2 4 3 7 7 9 8 8
Sample Output
4 3 4 3 2

模板題,而後剛剛已經講過了這個代碼
惟一的坑就在於接下來N+1行都是數字Ai
也就是有n+1數字Ai
也就是n須要n++

#include <bits/stdc++.h>
using namespace std;
int n;
int a[101000];
int q;
int blo;
int pos[101000];
int m[101000];
//blo爲區間大小,pos[i]表示a[i]元素位於第pos[i]塊,m[i]表示區塊最大值
int main(){
	cin>>n;
	n++;
	blo=sqrt(n);
	for(int i=1;i<=n;i++){
		cin>>a[i];
		pos[i]=(i-1)/blo+1;
		m[pos[i]]=max(a[i],m[pos[i]]);
	}
	cin>>q;
	int l,r;
	while(q--){
		cin>>l>>r;
		l++;
		r++;
		int ans=0;
		for(int i=l;i<=min(r, pos[l]*blo);i++){//統計左殘缺塊 
			ans=max(ans,a[i]);
		} 
		if(pos[l]!=pos[r]){//存在右殘缺塊 
			for(int i=(pos[r]-1)*blo+1;i<=r;i++){//統計右殘缺塊 
				ans=max(ans,a[i]);
			}
		} 
		for( int i=pos[l]+1;i<=pos[r]-1;i++){//統計中間整塊 
			ans=max(ans,m[i]);
		}	
		cout<<ans<<endl;
	}
	return 0;
}

教主的魔法

[Noip模擬題]教主的魔法
Description
教主最近學會了一種神奇的魔法,可以令人長高
因而他準備演示給XMYZ信息組每一個英雄看
因而N個英雄們又一次彙集在了一塊兒
此次他們排成了一列,被編號爲一、二、……、N
每一個人的身高一開始都是不超過1000的正整數
教主的魔法每次能夠把閉區間[L, R](1≤L≤R≤N)內的英雄的身高所有加上一個整數W
(雖然L=R時並不符合區間的書寫規範,但咱們能夠認爲是單獨增長第L(R)個英雄的身高)
CYZ、光哥和ZJQ等人不信教主的邪
因而他們有時候會問WD閉區間 [L,R] 內有多少英雄身高大於等於C
以驗證教主的魔法是否真的有效
WD巨懶,因而他 把這個回答的任務交給了你
Input
第1行爲兩個整數N、Q。Q爲問題數與教主的施法數總和
第2行有N個正整數,第i個數表明第i個英雄的身高
第3到第Q+2行每行有一個操做:
(1)若第一個字母爲"M",則緊接着有三個數字L、R、W
表示對閉區間 [L, R]內全部英雄的身高加上W
(2)若第一個字母爲"A",則緊接着有三個數字L、R、C
詢問閉區間 [L, R] 內有多少英雄的身高大於等於C
N≤1000000,Q≤3000,1≤W≤1000,1≤C≤1,000,000,000
Output
對每一個"A"詢問輸出一行,僅含一個整數,表示閉區間 [L, R] 內身高大於等於C的英雄數。Sample Input
5 3 1 2 3 4 5 A 1 5 4 M 3 5 1 A 1 5 4
Sample Output
2 3
【輸入輸出樣例說明】
原先5個英雄身高爲一、二、三、四、5,此時[1, 5]間有2個英雄的身高大於等於4
教主施法後變爲一、二、四、五、6,此時[1, 5]間有3個英雄的身高大於等於4

不少元素,進行增長、查找最大值操做

多了一個修改操做,不是很難
同理像查找這樣整塊維護,殘塊增長
咱們就再增長一個數組,統一記錄每一個整塊變化量是多少
記錄每一個整塊的變化量,而後最後找最值的時候,單個整塊的最值加上或者減去變化量比較就能夠了
殘塊的單個元素直接加上或者減去變化量,找最值

void update(int x,int y,int v){
    if(pos[x]==pos[y]){
        for(int i=x;i<=y;i++)a[i]=a[i]+v;
    }
    else{
        for(int i=x;i<=pos[x]*block;i++)a[i]=a[i]+v;
        for(int i=(pos[y]-1)*block+1;i<=y;i++)a[i]=a[i]+v;
    }
    reset(pos[x]);reset(pos[y]);
    for(int i=pos[x]+1;i<pos[y];i++)
       add[i]+=v;
}

#include<bits/stdc++.h>
using namespace std;
int n;
int q,m,block;
int a[1010000],b[1010000],pos[1010000],add[1010000];
void reset(int x){
    int l=(x-1)*block+1,r=min(x*block,n);
    for(int i=l;i<=r;i++)
        b[i]=a[i];
    sort(b+l,b+r+1);
}
int find(int x,int v){
    int l=(x-1)*block+1,r=min(x*block,n);
    int last=r;
    while(l<=r){
        int mid=(l+r)>>1;
        if(b[mid]<v)l=mid+1;
        else r=mid-1;
    }
    return last-l+1;
}
void update(int x,int y,int v){
    if(pos[x]==pos[y]){
        for(int i=x;i<=y;i++)a[i]=a[i]+v;
    }
    else{
        for(int i=x;i<=pos[x]*block;i++)a[i]=a[i]+v;
        for(int i=(pos[y]-1)*block+1;i<=y;i++)a[i]=a[i]+v;
    }
    reset(pos[x]);reset(pos[y]);
    for(int i=pos[x]+1;i<pos[y];i++)
       add[i]+=v;
}
int query(int x,int y,int v){
    int sum=0;
    if(pos[x]==pos[y]){
        for(int i=x;i<=y;i++)if(a[i]+add[pos[i]]>=v)sum++;
    }
    else {
        for(int i=x;i<=pos[x]*block;i++)
            if(a[i]+add[pos[i]]>=v)sum++;
        for(int i=(pos[y]-1)*block+1;i<=y;i++)
            if(a[i]+add[pos[i]]>=v)sum++;
    }
    for(int i=pos[x]+1;i<pos[y];i++)
        sum+=find(i,v-add[i]);
    return sum;
}
int main(){
    cin>>n>>q;
    block=int(sqrt(n));
    for(int i=1;i<=n;i++){
        cin>>a[i];
        pos[i]=(i-1)/block+1;
        b[i]=a[i];
    }
    if(n%block)m=n/block+1;
    else m=n/block;
    for(int i=1;i<=m;i++)reset(i);
    for(int i=1;i<=q;i++){
        char ch[5];int x,y,v;
        cin>>ch>>x>>y>>v;
        if(ch[0]=='M'){
        	update(x,y,v);
		}else{
			cout<<query(x,y,v)<<endl;
		}
    }
    return 0;
}

END

相關文章
相關標籤/搜索