1 条题解

  • 0
    @ 2026-10-31 17:58:08

    思路

    对每个社团 kk,取消它的摊位后,空闲格数等于

    20002−(被覆盖格子总数−只被 k 覆盖的格子数)2000^2-(\text{被覆盖格子总数}-\text{只被 }k\text{ 覆盖的格子数})

    所以只需要统计:操场中完全没被覆盖的格子数 bk[0]bk[0],以及每个社团单独覆盖(只被它一个摊位覆盖)的格子数 bk[k]bk[k]。答案就是 bk[0]+bk[k]bk[0]+bk[k]——这些格子原本被覆盖,取消该摊位后都会变为空闲。

    用二维差分维护两个数组:

    • a[i][j]:覆盖 (i,j)(i,j) 的摊位个数;
    • b[i][j]:覆盖 (i,j)(i,j) 的摊位编号之和。

    对第 kk 个矩形执行四次差分:a 的角上 ±1\pm1,b 的角上 ±k\pm k。做完二维前缀和后:

    • a[i][j]==0:该格从未被覆盖,直接计入 bk[0]bk[0];
    • a[i][j]==1:该格恰好被一个摊位覆盖,此时 b[i][j] 就是那个摊位的编号(求和里只有它一项),计入 bk[k]bk[k];
    • a[i][j]>=2:取消任何一个摊位后该格仍被覆盖,与答案无关,不统计 b。

    注意只在 a[i][j]==1 时读取 b[i][j];覆盖数大于 11 时编号和可能恰好等于某个编号(例如 1+2=31+2=3),不能用它判断归属。

    最后对每个 kk 输出 bk[0]+bk[k]bk[0]+bk[k]。

    时间复杂度 O(N+20002)O(N+2000^2),空间 O(20002)O(2000^2)(两份 2020×20202020\times2020 的 long long 差分数组约 65 MB)。

    代码

    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    
    const int M = 2020;   // 坐标最大 2000,多留几个位置给差分边界
    void go(){
        vector<vector<int>> a(M,vector<int>(M,0));   // 每个格子被多少摊位覆盖
        vector<vector<int>> b(M,vector<int>(M,0));   // 覆盖该格子的摊位编号之和
    
        int n;
        cin >> n;
        for(int k=1;k<=n;k++){
            int u,d,l,r;
            cin >> u >> d >> l >> r;
            // 覆盖次数的二维差分:四个角分别加减 1
            a[u][l]++, a[d+1][r+1]++;
            a[u][r+1]--, a[d+1][l]--;
            // 摊位编号和的二维差分:四个角分别加减 k
            b[u][l]+=k, b[d+1][r+1]+=k;
            b[u][r+1]-=k, b[d+1][l]-=k;
        }
    
        // 二维前缀和还原出每个格子的真实值
        for(int i=1;i<M;i++){
            for(int j=1;j<M;j++){
                a[i][j] += a[i-1][j]+a[i][j-1]-a[i-1][j-1];
                b[i][j] += b[i-1][j]+b[i][j-1]-b[i-1][j-1];
            }
        }
    
        vector<int> bk(n+1,0);   // bk[0] 统计未覆盖格,bk[k] 统计只被 k 覆盖的格
        for(int i=1;i<=2000;i++){
            for(int j=1;j<=2000;j++){
    
                if(a[i][j]==0){bk[0]++;}              // 从未被覆盖
                else if(a[i][j]==1){bk[b[i][j]]++;}   // 只被一个摊位覆盖,b 即其编号
            }
        }
    
        // 取消第 i 个摊位后变成空闲的格子数
        for(int i=1;i<=n;i++){
            cout << bk[0]+bk[i] << "\n";
        }
    }
    
    signed main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);cout.tie(nullptr);
        int _=1;
        // 本题只有一组数据,不需要读入测试组数
        while(_--){
            go();
        }
        return 0;
    }
    
    • 1

    信息

    ID
    27
    时间
    1000ms
    内存
    256MiB
    难度
    3
    标签
    递交数
    0
    已通过
    0
    上传者