来一发简单做法
题目链接:P9286 [ROI 2018] Extraction of radium
通过读题目,我们不难想到,找到既是横向最大值又是纵行最大值的位置,可以单独处理横向和纵向,满足一个方向的最大值就标记一次,那么标记两次的位置就是当前局面的一个可行点。这样静态操作就明晰了。
现在考虑动态操作,把一个点的值改的比之前大,如果之前这个点事可行的,改的更大之后一定依旧满足。如果之前不是可行点,可是现在比所在列的最大值大,或者比所在行的最大值大,那么就更新标记。
更新标记的操作无非就是把之前的标记抹去,添加新标记,用 \(x\) 数组储存每一行的最大值所在列,用 \(y\) 数组储存每一列的最大值所在行,更新一下 \(x\) 数组和 \(y\) 数组即可。
这里使用动态数组来实现,注意下标。
见代码:
#include<iostream>
#include<cstdio>
#include<queue>
#include<cmath>
#include<cstring>
#include<vector>
using namespace std;
int x[200001],y[200001];
vector<int>a[200001];
vector<int>vis[200001];
int main()
{
int n, m, q, ans = 0;
scanf("%d%d%d",&n, &m, &q);
for(int i = 0;i <= n - 1; i++){
a[i].resize(m,0);
vis[i].resize(m,0);
for(int j = 0;j <= m - 1; j++){
scanf("%d",&a[i][j]);
if(a[i][j] > a[i][x[i]]){
x[i] = j;
}
if(a[i][j] > a[y[j]][j]){
y[j] = i;
}
}
}
for(int i = 0;i <= n - 1; i++){
vis[i][x[i]]++;
}
for(int j = 0;j <= m-1; j++){
vis[y[j]][j]++;
}
for(int i = 0;i <= n - 1; i++){
for(int j = 0;j <= m - 1; j++){
if(vis[i][j] == 2) ans++;
}
}
for(int i, j, w, o = 1;o <= q; o++){
scanf("%d%d%d",&i, &j, &w);
i--;
j--;
a[i][j] = w;
if(vis[i][j] == 2)
{
printf("%d\n",ans);
continue;
}
if(a[i][j] > a[i][x[i]]){
if(vis[i][x[i]] == 2){
ans--;
}
vis[i][x[i]]--;
x[i] = j;
vis[i][x[i]]++;
if(vis[i][x[i]] == 2){
ans++;
}
}
if(a[i][j] > a[y[j]][j]){
if(vis[y[j]][j] == 2){
ans--;
}
vis[y[j]][j]--;
y[j] = i;
vis[y[j]][j]++;
if(vis[y[j]][j] == 2){
ans++;
}
}
printf("%d\n",ans);
}
return 0;
}
标签:ROI,标记,int,最大值,vis,2018,ans,P9286,include
From: https://www.cnblogs.com/iruiforever/p/17464212.html