J. Carpets Removal
题意: 在 m × m m \times m m×m 的房间里有 n n n块地毯,给出每块地毯的位置,问恰好取走其中两块地毯时房间内仍然有被地毯覆盖的格子数
n ≤ 3 e 5 , m ≤ 1500 , ∑ n ≤ 2 e 6 , ∑ m ≤ 5 e 7 n \le 3e5, m \le 1500, \sum n \le 2e6, \sum m \le 5e7 n≤3e5,m≤1500,∑n≤2e6,∑m≤5e7 , 6s
容易会想到往格子上靠,如果一个格子被覆盖了三次及以上它就救不回来了
快速求被一块地毯覆盖的方案数和被两块地毯覆盖的方案数,还不够,还需要知道是哪块地毯覆盖了这个格子
我刚开始想的是线段树,扫描线之类但感觉就算可以也会很难写
可以用二维差分+前缀和处理,记录每个格子被覆盖的次数,每个格子上的编号和
当一个格子被一块地毯覆盖的时候,编号和就恰好是格子的编号,如果被两块地毯覆盖,不能得出地毯编号,这里就很妙:记录每个格子编号的平方和
当一个格子被两块地毯覆盖的时候,设为 x , y x, y x,y
已
知
:
x
+
y
,
x
2
+
y
2
已知:x+y , x^2 + y^2
已知:x+y,x2+y2
求
:
x
,
y
求:x, y
求:x,y
x
−
y
=
(
x
2
+
y
2
)
−
(
(
x
+
y
)
2
−
(
x
2
+
y
2
)
)
x - y = \sqrt{(x^2+y^2) -((x + y)^2 - (x^2+y^2))}
x−y=(x2+y2)−((x+y)2−(x2+y2))
x
=
x
+
y
+
(
x
−
y
)
2
x = \frac{x+ y + (x - y)}{2}
x=2x+y+(x−y)
y
=
(
x
+
y
)
−
x
y = (x+y)- x
y=(x+y)−x
代码注意初始化和差分时代码。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pii;
const ll N = 3e5 + 10;
const ll M = 1510;
ll cnt[M][M], sum[M][M], pow2[M][M];
ll s1[N];
map<pii, ll> mp;
ll n, m;
inline void init() {
for (ll i = 0; i <= n; ++i) s1[i] = 0;
for (ll i = 1; i <= m; ++i)
for (ll j = 1; j <= m; ++j)
cnt[i][j] = sum[i][j] = pow2[i][j] = 0;
mp.clear();
}
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
ll T;
cin >> T;
while (T--) {
cin >> n >> m;
init();
for (ll i = 1; i <= n; ++i) {
ll xl, xr, yl, yr;
cin >> xl >> xr >> yl >> yr;
cnt[xl][yl]++;
cnt[xl][yr + 1]--;
cnt[xr + 1][yl]--;
cnt[xr + 1][yr + 1]++;
sum[xl][yl] += i;
sum[xl][yr + 1] -= i;
sum[xr + 1][yl] -= i;
sum[xr + 1][yr + 1] += i;
pow2[xl][yl] += i * i;
pow2[xl][yr + 1] -= i * i;
pow2[xr + 1][yl] -= i * i;
pow2[xr + 1][yr + 1] += i * i;
}
for (ll i = 1; i <= m; ++i) {
for (ll j = 1; j <= m; ++j) {
cnt[i][j] += cnt[i - 1][j] + cnt[i][j - 1] - cnt[i - 1][j - 1];
sum[i][j] += sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1];
pow2[i][j] += pow2[i - 1][j] + pow2[i][j - 1] - pow2[i - 1][j - 1];
}
}
ll s0 = 0, mx1 = 0, mx2 = 0;
for (ll i = 1; i <= m; ++i) {
for (ll j = 1; j <= m; ++j) {
if (cnt[i][j] == 0) s0++;
else if (cnt[i][j] == 1) s1[sum[i][j]]++;
else if (cnt[i][j] == 2) {
ll a = (sqrt(pow2[i][j] - (sum[i][j] * sum[i][j] - pow2[i][j])) + sum[i][j]) / 2;
ll b = sum[i][j] - a;
if (a > b) swap(a, b);
mp[{a, b}]++;
}
}
}
for (ll i = 1; i <= n; ++i) {
ll tmp = s1[i];
if (tmp > mx2) swap(tmp, mx2);
if (mx2 > mx1) swap(mx2, mx1);
}
ll mxres = mx1 + mx2;
for (auto tmp: mp) {
ll res = s1[tmp.first.first] + s1[tmp.first.second] + tmp.second;
mxres = max(mxres, res);
}
cout << m * m - s0 - mxres << endl;
}
return 0;
}