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]);
}
希望能帮助到大家!