ABC278E 题解

前言

题目传送门!

或许更好的阅读体验?

非常套路的题目,为啥要放在 E。

思路

容易发现,相邻查询的覆盖区间不会差太远。所以考虑用较短的时间处理两个查询。

思路也很容易想到:维护两个操作 add 与 del,支持 \(O(1)\) 增加、删除一个数。

void add(int x)
{
    if (!vis[x]) ans++;
    vis[x]++;
}
void del(int x)
{
    vis[x]--;
    if (!vis[x]) ans--;
}

然后,对于相邻两个操作,只需要删除掉前一列,并增加新的一列即可。

只需要特殊处理一下第一个区间即可。

这样,时间复杂度就是 \(O(n^3)\) 了。

代码

省去了大段的缺省源。

const int N = 305;
int vis[N], zlt[N]; //zlt 是备份的原 vis
int a[N][N];

int ans, tans; //tans 是备份的原 ans
void add(int x)
{
    if (!vis[x]) ans++;
    vis[x]++;
}
void del(int x)
{
    vis[x]--;
    if (!vis[x]) ans--;
}
int ANS[N][N];
void solve()
{
    int n, m, k, h, w; 
    scanf("%d%d%d%d%d", &n, &m, &k, &h, &w);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
        {
            scanf("%d", &a[i][j]);
            if (!vis[a[i][j]]) ans++, tans++;
            vis[a[i][j]]++, zlt[a[i][j]]++;
        }
    for (int i = 1; h + i - 1 <= n; i++)
    {
        ans = tans;
        for (int j = 1; j <= k; j++) vis[j] = zlt[j];

        for (int j = i; j <= h + i - 1; j++)
            for (int k = 1; k <= w; k++)
                del(a[j][k]);
        ANS[i][1] = ans;
        for (int j = 1; j + w - 1 <= m; j++) //纵坐标
        {
            for (int k = i; k <= h + i - 1; k++) add(a[k][j]), del(a[k][j + w]); //横坐标
            ANS[i][j + 1] = ans; //统计答案
        }
    }
    for (int i = 1; i <= n - h + 1; i++, putchar('\n'))
        for (int j = 1; j <= m - w + 1; j++, putchar(' '))
            printf("%d", ANS[i][j]);
}

希望能帮助到大家!

posted @ 2022-11-21 09:42  liangbowen  阅读(28)  评论(0编辑  收藏  举报