題目背景
此題約為noip提高組day2t1難度。
題目描述
在n*n的格仔上有m個地毯。
給出這些地毯的資訊,問每個點被多少個地毯覆蓋。
輸入輸出格式
輸入格式:
第一行,兩個正整數n、m。意義如題所述。
接下來m行,每行兩個座標(x1,y1)和(x2,y2),代表一塊地毯,左上角是(x1,y1),右下角是(x2,y2)。
輸出格式:
輸出n行,每行n個正整數。
第i行第j列的正整數表示(i,j)這個格仔被多少個地毯覆蓋。
輸入輸出樣例
輸入樣例#1:
5 32 2 3 3
3 3 5 5
1 2 1 4
輸出樣例#1:
0 1 1 1 0
0 1 1 0 0
0 1 2 1 1
0 0 1 1 1
0 0 1 1 1
說明樣例解釋
0 0 0 0 0 0 0 0 0 0 0 1 1 1 0
0 1 1 0 0 0 1 1 0 0 0 1 1 0 0
0 1 1 0 0 -> 0 1 2 1 1 -> 0 1 2 1 1
0 0 0 0 0 0 0 1 1 1 0 0 1 1 1
0 0 0 0 0 0 0 1 1 1 0 0 1 1 1
資料範圍
對於20%的資料,有n<=50,m<=100。
對於100%的資料,有n<=1000,m<=1000。
【題意】:略
【分析】://by阮行止
n*n的矩陣,每次給乙個子矩陣 區間+1 。最後輸出整個矩陣。
優化。用二維線段樹或二維樹狀陣列完成上面的操作。
足以吊打此題。
但是noip是不會考二維資料結構的
考慮這個問題的一維版:乙個序列,最開始全是 0 .每次區間加 1 ,最後輸出每個數。
於是有一種叫做「差分」的奇技淫巧:
假設我們現在要給[2,5]這個區間加一。原來的序列是:
0 0 0 0 0 0 0 0
這時候我們在2上面打 +1 標記, 6 上面打 -1 標記。那麼現在的序列是:
0 +1 0 0 0 -1 0
有什麼用呢?從左往右掃瞄這個陣列,記錄當前經過的標籤之和。這個和就是對應那個數的答案。
這樣,對於每個區間加操作,只需要o(1) 的時間打上標記。
最後掃瞄輸出即可。
現在把問題拓展到二維。假設我們要覆蓋[(2,2),(5,5)] ,那麼標記便可以這樣打:
0 0 0 0 0 0
0 +1 0 0 0 -1
0 +1 0 0 0 -1
0 +1 0 0 0 -1
0 +1 0 0 0 -1
0 0 0 0 0 0
即在每一行都按照一維的方式來操作
int sum=0,i,j;
for(i=1;i<=n+1;i++)
for(j=1;j<=n+1;j++)
sum+=flag[i][j],real[i][j]=sum;
之後 real 陣列裡就存了最後的矩陣。輸出即可。
這個演算法的複雜度是每次打標記o(n) ,總複雜度是o(mn+n^2)
【程式碼】:[一維]
#include#include#include#include#include#include#include#include#include#include//#includeusing namespace std;
typedef long long ll;
int a[1005][1005];
int x[1005][1005];
const int maxn=1e5+10;
void fun(int x1,int y1,int x2, int y2)
}}int main()
}for(int i=1;i<=n;i++)
printf("\n");
}}}/*
5 32 2 3 3
3 3 5 5
1 2 1 4
0 1 1 1 0
0 1 1 0 0
0 1 2 1 1
0 0 1 1 1
0 0 1 1 1
0 0 0 0 0 0 0 0 0 0 0 1 1 1 0
0 1 1 0 0 0 1 1 0 0 0 1 1 0 0
0 1 1 0 0 -> 0 1 2 1 1 -> 0 1 2 1 1
0 0 0 0 0 0 0 1 1 1 0 0 1 1 1
0 0 0 0 0 0 0 1 1 1 0 0 1 1 1
*/
首先這題的主要思想很多大佬都講了:就是差分,但是我的寫法和他們的寫法又不一樣。
本題資料範圍為n<=1000,m<=1000。
當n<=1000,m<=1000000甚至m<=10000000怎麼辦呢?
這時不管是o(n)的修改,還是甚至是 o(log^2(n))o(log
2 (n)) ,都是跑不過的。
而我們有乙個o(1)修改的做法:二維差分。
設b[i][j]=a[i][j]-a[i-1][j]-a[i][j-1]+a[i-1][j-1]。
這樣每次修改b[i][j]相當於對任意 i\le x,j \le yi≤x,j≤y 對a[x][y]做同樣的修改
然後每次修改就直接++b[x1][y1],--b[x2+1][y1],--b[x1][y2+1],++b[x2+1][y2+1]即可。
也就是用 o(1)o(1) 的複雜度表示 o(n^2)o(n
2 ) 的覆蓋(原話來自下面那篇題解「用o(1)複雜度來表示o(n)的覆蓋」)
最後再直接a[i][j]=a[i-1][j]+a[i][j-1]-a[i-1][j-1]+b[i][j]還原出原序列即可。
/* author: cnyali_lk
lang: c++
prog: 3397.cpp
*/
[二維]
我們借助這個研究一下。假設在這個矩陣(二維陣列)中,我們要求和的是上圖中紅色區域。現在我們已經預處理出了所有點的字首和,現在給定兩個點(x1,y1),(x2,y2),我們要求 以這兩個點連線為對角線的乙個子矩陣的數值之和。暴力做法直接挨個加這個我就不再多說了,反正早晚都得tle,我們重點考慮用字首和的快速做法。
首先我們可以把s[x2][y2]求出來,它代表整個大矩形的字首和,然後我們分別減去它左邊多出來的一塊的字首和和下邊多出來一塊的字首和,這樣就是最終答案了?
不是!這不是最終答案。可以發現,在我們剪掉這兩個多出的區域時,下邊的一小塊被減了兩次,但減兩次顯然是不合理的,我們應該加回來。。
所以對於一次的查詢答案ans應該等於s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]+s[x1-1][y1-1]。
這個二維字首和也稱差分序列。
洛谷 P3397 地毯(二維差分)
題目描述 在n n的格仔上有m個地毯。給出這些地毯的資訊,問每個點被多少個地毯覆蓋。輸入格式 第一行,兩個正整數n m。意義如題所述。接下來m行,每行兩個座標 x1,y1 和 x2,y2 代表一塊地毯,左上角是 x1,y1 右下角是 x2,y2 輸出格式 輸出n行,每行n個正整數。第i行第j列的正整...
P3397 地毯(二維差分)
題意 在乙個n n的地圖中,有m張地毯,每張矩形地毯的左上角和右下角已知,輸出最後整個地圖的每個點有多少張地毯覆蓋。思路 二維差分很明顯。二維字首和 二維線段樹也可以做。我們首先要用乙個陣列,記錄變化。對於 x1,y1 到 x2,y2 這一區間全部加1的話,我們需要a x1 y1 1,a x2 1 ...
P3397 地毯 差分
題目背景 此題約為noip提高組day2t1難度。題目描述 在 n nn times nn n 的格仔上有 mmm 個地毯。給出這些地毯的資訊,問每個點被多少個地毯覆蓋。輸入格式 第一行,兩個正整數 n,mn,mn,m。意義如題所述。接下來 mmm 行,每行兩個座標 x1,y1 x 1,y 1 x1...