icpc 2018 焦作 J. Carpets Removal

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 n3e5,m1500,n2e6,m5e7 , 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))} xy=(x2+y2)((x+y)2(x2+y2))
x = x + y + ( x − y ) 2 x = \frac{x+ y + (x - y)}{2} x=2x+y+(xy)
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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值