地毯
题目描述
在 n × n n\times n n×n 的格子上有 m m m 个地毯。
给出这些地毯的信息,问每个点被多少个地毯覆盖。
输入格式
第一行,两个正整数 n , m n,m n,m。意义如题所述。
接下来 m m m 行,每行两个坐标 ( x 1 , y 1 ) (x_1,y_1) (x1,y1) 和 ( x 2 , y 2 ) (x_2,y_2) (x2,y2),代表一块地毯,左上角是 ( x 1 , y 1 ) (x_1,y_1) (x1,y1),右下角是 ( x 2 , y 2 ) (x_2,y_2) (x2,y2)。
输出格式
输出 n n n 行,每行 n n n 个正整数。
第 i i i 行第 j j j 列的正整数表示 ( i , j ) (i,j) (i,j) 这个格子被多少个地毯覆盖。
样例 #1
样例输入 #1
5 3
2 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 0 | 0 0 0 |
---|---|---|---|---|
0 0 0 | 1 1 1 | 1 1 1 | 0 0 0 | 0 0 0 |
0 0 0 | 1 1 1 | 1 1 1 | 0 0 0 | 0 0 0 |
0 0 0 | 0 0 0 | 0 0 0 | 0 0 0 | 0 0 0 |
0 0 0 | 0 0 0 | 0 0 0 | 0 0 0 | 0 0 0 |
覆盖第一、二个地毯后:
0 0 0 | 0 0 0 | 0 0 0 | 0 0 0 | 0 0 0 |
---|---|---|---|---|
0 0 0 | 1 1 1 | 1 1 1 | 0 0 0 | 0 0 0 |
0 0 0 | 1 1 1 | 2 2 2 | 1 1 1 | 1 1 1 |
0 0 0 | 0 0 0 | 1 1 1 | 1 1 1 | 1 1 1 |
0 0 0 | 0 0 0 | 1 1 1 | 1 1 1 | 1 1 1 |
覆盖所有地毯后:
0 0 0 | 1 1 1 | 1 1 1 | 1 1 1 | 0 0 0 |
---|---|---|---|---|
0 0 0 | 1 1 1 | 1 1 1 | 0 0 0 | 0 0 0 |
0 0 0 | 1 1 1 | 2 2 2 | 1 1 1 | 1 1 1 |
0 0 0 | 0 0 0 | 1 1 1 | 1 1 1 | 1 1 1 |
0 0 0 | 0 0 0 | 1 1 1 | 1 1 1 | 1 1 1 |
数据范围
对于 20 % 20\% 20% 的数据,有 n ≤ 50 n\le 50 n≤50, m ≤ 100 m\le 100 m≤100。
对于 100 % 100\% 100% 的数据,有 n , m ≤ 1000 n,m\le 1000 n,m≤1000。
题解
做这道题之前我还不是很懂差分的,于是去搜了下差分,我从【C++算法基础】#9前缀和与差分 | 轻松学算法 | 图解ACM竞赛算法
学习了差分和前缀和的关系,以及差分的一些简单的应用。
经过学习之后得出这题的解题思路。如果嫌看视频有点慢,只想学习一下差分是什么,也可以只看我的文章,希望有帮助。
首先是差分的应用场景:
差分是用于求数组部分区间(有区间重叠)变化后的数组的。
什么意思呢?就是比如说对数组的
[
1
,
3
]
、
[
2
,
6
]
[1,3]、[2,6]
[1,3]、[2,6]两个区间分别进行
+
2
、
−
4
+2、-4
+2、−4的操作,这两个区间有重叠部分,如果是正常的方法就是有k个区间修改,枚举k次,每次枚举修改一次。时间复杂度为
O
(
n
k
)
O(nk)
O(nk)。
而差分呢可以把这k次枚举直接消掉,我们只需要枚举1次,就可以完成对区间的修改!
差分维护一个差分数组
d
i
f
f
[
n
]
diff[n]
diff[n],这个数组里面记录着数组的变化,
首先
d
i
f
f
[
i
]
=
a
[
i
]
−
a
[
i
−
1
]
diff[i] = a[i] - a[i-1]
diff[i]=a[i]−a[i−1]
例如:
我们想要让区间
[
2
,
3
]
[2,3]
[2,3]的数
+
2
+2
+2
那么令
d
i
f
f
[
2
]
=
2
,
d
i
f
f
[
4
]
=
−
2
diff[2] = 2, diff[4] = -2
diff[2]=2,diff[4]=−2,为什么这么操作?
因为
d
i
f
f
[
i
]
=
a
[
i
]
−
a
[
i
−
1
]
diff[i] = a[i] - a[i-1]
diff[i]=a[i]−a[i−1]
所以
a
[
i
]
=
a
[
i
−
1
]
+
d
i
f
f
[
i
]
a[i] = a[i-1] + diff[i]
a[i]=a[i−1]+diff[i]
我们可以看到,当我们
令
d
i
f
f
[
2
]
=
2
diff[2] =2
diff[2]=2后,
a
[
2
]
=
a
[
1
]
+
d
i
f
f
[
2
]
a[2] = a[1] + diff[2]
a[2]=a[1]+diff[2]即
a
[
2
]
a[2]
a[2]比原来的
a
[
2
]
a[2]
a[2]多
2
2
2,
a
[
3
]
=
a
[
2
]
+
d
i
f
f
[
3
]
a[3] = a[2] + diff[3]
a[3]=a[2]+diff[3],因为
a
[
2
]
a[2]
a[2]已经比原来的多
2
2
2了,所以此时新的
a
[
3
]
a[3]
a[3]就比旧的也多
2
2
2。
以此类推,当我们修改 d i f f [ 2 ] = 2 diff[2]=2 diff[2]=2后,后续所有 a [ i > = 2 ] a[i >=2] a[i>=2]都比原先的多 2 2 2
可是我们只想修改区间 [ 2 , 3 ] [2,3] [2,3],那么我们需要让后续的 a [ i > 3 ] a[i>3] a[i>3]抵消掉多出来的 2 2 2?
方法也很简单,就是把 d i f f [ 4 ] diff[4] diff[4]修改成 − 2 -2 −2即可。
看完差分的思想后,这题就没什么特别大的难度了,我们只需要把每行的差分数组求出来,然后边还原新的数组就可以得到每个格子覆盖的地毯数量
N, M = map(int, input().strip().split())
diff = [[0 for _ in range(N+1)] for _ in range(N)]
coordinates = [list(map(int, input().strip().split())) for _ in range(M)]
for i in coordinates:
for k in range(i[0], i[2]+1):
diff[k-1][i[1]-1] += 1
diff[k-1][i[3]] -= 1
matrix = [[0 for _ in range(N)] for _ in range(N)]
for i in range(N):
for j in range(N):
matrix[i][j] = matrix[i][j-1] + diff[i][j]
print("\n".join([" ".join(list(map(str, mi))) for mi in matrix]))
这题显然还可以优化,等我什么时候变聪明了再回来优化吧。优化思路就是列也差分,那样维护
d
i
f
f
[
n
]
diff[n]
diff[n]只需要
O
(
m
)
O(m)
O(m)时间复杂度,而我的代码维护
d
i
f
f
[
n
]
diff[n]
diff[n]需要
O
(
m
n
)
O(mn)
O(mn)时间复杂度。
具体怎么维护后续又怎么还原我还没搞懂,这个大佬们轻喷。