2021-01-24更
差分與字首和
差分:
T1 堆疊草堆
題目:
Bessie對她自己最近在農場周圍的惡作劇感到抱歉,於是她同意幫助Farmer John堆疊新送達的一批乾草捆。
開始時有N(N是奇數)個空草堆,標記為1…N。FJ將會給Bessie包含K條指令的序列,每條指令的格式為"A B",表示Bessie應該在草堆A…B中每一個草堆頂部新放一捆乾草。例如,如果Bessie聽到指令"10 13",那麼她應該在草堆10,11,12和13上各新放一捆乾草。
在Bessie完成了FJ的所有指令後,FJ想要知道N個草堆高度的中位數——也就是說,所有草堆排序後中央草堆(由於N是奇數,這個草堆是唯一的)的高度。請幫助Bessie確定FJ問題的答案。
第1行:兩個被空格分隔的整數N和K。
第2…1+K行:每行包含一條FJ的指令,其格式為兩個被空格分隔的整數A和B。
輸出
一個整數, Bessie 完成所有指令後草堆高度的中位數。
輸入樣例
7 4
5 5
2 4
4 6
3 5
輸出樣例
1
思路:
看到這個題目後,我第一時間是想到用迴圈把每次輸入的草堆挨個加1,但後來發現TLE了,就想起了用差分,在每次要加的第一個草堆處加1,再最後一個的後面一個-1,最後統一加回去,十分快速簡便。
程式碼如下:
#include<bits/stdc++.h>
using namespace std;
int n,q,h,a[1000010],b[ 1000010];
void insert(int l,int r)
{
b[l]+=1;
b[r+1]-=1;
} //差分函式
int main()
{
ios::sync_with_stdio(false);//加快輸入輸出
cin>>n>>q;
for(int i=1;i<=q;i++)
{
int l,r;
cin>>l>>r;
insert(l,r);//對l~r的區間草堆加1
}
for(int i=1;i<=n;i++)
b[i]+=b[i-1];//字首和
sort(b+1,b+1+n);
cout<< b[n/2+1];
return 0;
}
T2 套圈遊戲
題目:
小b在逛公園的時候發現了一個好玩的遊戲,有一個 n*n 的方格,每個方格里面有若干硬幣,你只要交完錢,老闆就給你 m 個方形的圈,你扔出去的圈會覆蓋部分方格,這些方格你可以從中獲得一枚硬幣,但是老闆不會做虧本生意的,他規定:如果一個方格被覆蓋2次或以上,則不能獲得這個方格中的硬幣。
現在已知小b扔出去的 m 個圈的左上角以及右下角座標,請計算出小b最終獲得多少枚硬幣。
輸入
第一行,兩個整數n和m。(2<=n<=2000)
接下來m行,每行4個數,分別表示圈的左上角座標以及右下角座標。(座標值均在1~n之間)
輸出
一個整數,表示獲得的硬幣數。
輸入樣例
5 3
2 2 3 3
3 3 5 5
1 2 1 4
輸出樣例
14
思路:
這道題目很明顯要用二維差分來做,二維差分其實和二維字首和(見T4)類似,能快速的實現多個數據的改變。
程式碼如下:
#include<bits/stdc++.h>
using namespace std;
int n,m,q,h,b[2010][2010];
void insert(int x1,int y1,int x2,int y2)
{
b[x1][y1]+=1;
b[x2+1][y1]-=1;
b[x1][y2+1]-=1;
b[x2+1][y2+1]+=1;
} //二維差分函式
int main()
{
ios::sync_with_stdio(false);
cin>>n>>q;
while(q--)
{
int x1,y1,x2,y2;
cin>>x1>>y1>>x2>>y2;
insert(x1,y1,x2,y2);//對(x1,y1)~(x2,y2)範圍內的圈子套中次數加1
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];//字首和
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(b[i][j]==1)
h++;
cout<<h;
return 0;
}
字首和
T3 好奇的金魚(二)
題目:
有n條金魚排成一隊,現在已知每條魚的體重w。
好奇的金魚會有m次詢問,每次詢問給出兩個值(l,r)
(r >= l),想計算出[l,r]之間的金魚體重之和。
輸入
第一行2個數,n和m,中間用空格分隔
(1 <= n, m <= 100000)。
之後n+m行,
第 1 至 n 行:每行一個數字w(0 <= w <= 1000)
第 n + 1 至 n + m 行:每行2個數字l,r,中間用空格分隔
(0 < l <= r <= n)
輸出
輸出共m行,每行一個數,對應詢問區間的金魚體重之和。
輸入樣例
3 3
1
3
5
1 2
1 3
2 3
輸出樣例
4
9
8
思路:
其實我第一次做這道題的時候還是懵B一個,上來就直接迴圈m次找答案,然後…就TLE了,後來我認真學習,知道了字首和這種方法,才A了這題。
字首和:一種找l~r區間的和的方法,很快。開始每次都把從a1到當前的和找到(s[n]=s[n-1]+a[n]),求l到r區間的時候直接把s[r]-s[l-1] (不然會減掉l)就可以得到了。
程式碼如下:
#include<bits/stdc++.h>
using namespace std;
int a[100010],s[100010],n,m,h,d,f;
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
s[i]=a[i]+s[i-1]; //字首和
}
for(int i=1;i<=m;i++)
{
cin>>d>>f;
h=s[f]-s[d-1];
cout<<h<<endl;
}
return 0;
}
T4 子矩陣求和
題目:
給出一個n * m的矩陣a,矩陣元素a[i][j]小於1000,進行q次查詢,每次查詢給出子矩陣的4個邊界(上下左右),求該子矩陣所有元素之和。
比如有一個查詢:1 2 1 3 ,表示要計算從第1行到第2行,從第1列到第3列的所有元素之和。
輸入
第一行2個整數n, m,中間用空格分割,分別對應陣列的行數n、列數m(1 <= m,n <= 1000)
接下來n行,每行m個整數表示矩陣的內容a[i][j] 。
(0 <= a[i,j] <= 1000)
接下來1行,一個數q,對應查詢的數量。(1 <= q <= 10000)
接下來q行,每行4個整數,對應子矩陣的上下左右邊界u,d,l,r。(1 <= u <= d <= n, 1 <= l <= r <= m)
輸出
共q行,對應q個詢問的求和結果。
輸入樣例
3 4
1 2 3 4
5 6 7 8
9 10 11 12
3
1 3 1 2
1 2 1 3
1 3 1 3
輸出樣例
33
24
54
思路:
第一次看見這道題的時候,我剛學二維陣列,強行用暴力解法(for迴圈)解,直接TLE,然後想起字首和是個好東西,就用一維字首和做了一下,完美的TLE了,想了半天,受到一維字首和的啟發,寫出了二維字首和,
順利AC
程式碼如下:
#include<bits/stdc++.h>
using namespace std;
int a[1010][1010],n,m,gg,u,d,l,r,s[1010][1010],h;
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cin>>a[i][j];
s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];
}
}
cin>>gg;
for(int i=1;i<=gg;i++)
{
cin>>u>>d>>l>>r;
cout<<s[d][r]-s[u-1][r]-s[d][l-1]+s[u-1][l-1]<<" ";
//二維字首和
}
return 0;
}