Codeforces Round 1067 (Div. 2) E Sink
事实上这题只需要维护每个极大连通块是局部最小值的数量。
事实上这个可以等价于每个点是局部最小值的数量的和。
直接维护即可,答案为0的个数。
注意维护技巧,修改的点可以看作独立的点,原来的答案如果其 size 为 0 就不考虑其的贡献,也就是 ans-- 。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using std::cin,std::cout;
const int dx[4]={0,1,0,-1},dy[4]={1,0,-1,0};
const int N=600020;
int n,m;
std::vector<int>a[N],b[N];
int f[N],val[N],si[N];
int find(int x){
while(x!=f[x])x=f[x]=f[f[x]];
return x;
}
int ans;
void merge(int x,int y){
if(!x||!y)return ;
x=find(x),y=find(y);
if(x==y)return ;
if(val[y]==0)ans--;
f[x]=y,val[y]+=val[x];
if(val[x]==0)ans--;
if(val[y]==0)ans++;
si[y]+=si[x];
}
void del(int x){
x=find(x);
val[x]--;
if(val[x]==0)ans++;
}
void add(int x){
x=find(x);
if(val[x]==0)ans--;
val[x]++;
}
void go(int x,int y,int t){
for(int d=0;d<4;d++){
int nx=x+dx[d],ny=y+dy[d];
if(nx<=0||nx>n||ny<=0||ny>m)continue;
if(a[nx][ny]<a[x][y]){
if(t==1)add(b[x][y]);else del(b[x][y]);
}
if(a[nx][ny]>a[x][y]){
if(t==1)add(b[nx][ny]);else del(b[nx][ny]);
}
}
if(t==1){
for(int d=0;d<4;d++){
int nx=x+dx[d],ny=y+dy[d];
if(nx<=0||nx>n||ny<=0||ny>m)continue;
if(a[nx][ny]==a[x][y]){
// if(t==1)add(b[x][y]);else del(b[x][y]);
merge(b[nx][ny],b[x][y]);
// cout<<nx<<" "<<ny<<" "<<x<<" "<<y<<"\n";
}
}
}
}
void work(){
int tot=0;
cin>>n>>m;
for(int i=0;i<=n+1;i++){
a[i].clear(),b[i].clear();
a[i].resize(m+1),b[i].resize(m+1);
}
ans=0;
ans=n*m;
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){
tot++;
f[tot]=tot,b[i][j]=tot,val[tot]=0;
si[tot]=1;
go(i,j,-1);
cin>>a[i][j];
go(i,j,1);
// cout<<ans<<"===\n";
}
cout<<ans<<'\n';
int q;
cin>>q;
while(q--){
int x,y,c;
cin>>x>>y>>c;
go(x,y,-1);
a[x][y]-=c;
si[find(b[x][y])]--;
if(si[find(b[x][y])]==0)ans--;
tot++;
b[x][y]=tot;
f[tot]=tot;
val[tot]=0;
si[tot]=1;
ans++;
go(x,y,1);
cout<<ans<<'\n';
}
}
int main(){
// std::ios::sync_with_stdio(0);
// cin.tie(0),cout.tie(0);
int t=1;
cin>>t;
while(t--){
work();
}
return 0;
}

浙公网安备 33010602011771号