題目連結
主要演算法:
線段樹(虛假的),奇技淫巧(真正的)
思路:
1.初步:考慮如何保證一個區間坐好後是一個矩形,有一個思路從另一個題中啟示我們維護 \(xmin,xmax,ymin,ymax\) ,但是這樣無法保證在中間挖一個空的情況(有一個別的題解,可以染色後維護四個角和一個判框的東西),但我們覺得就算可以維護這樣一次修改會改動很多東西,不太好做。
2.進一步:所以引入一個染色,在第\(i\)的時間將第\(i\)個人染成黑色,這時圖變成了黑白兩色,所以考慮黑點和白點的性質。對於左上角的黑點它的上下都是白點且其它黑點如果一個矩形都不滿足,對於每一個白點如果四聯通有兩個黑點,就會出現拐角,不是矩形。所以對於矩形,滿足條件的黑白點總數是1(一個黑色點+0個白色點),就可以寫出不交換時的程式.
int black(int x,int y)
{
int ans=zs+1;
for(int i=0;i<=1;i++)
{
int xx=x+dx[i],yy=y+dy[i];
if(xx>=1&&xx<=n&&yy>=1&&yy<=m)ans=min(ans,num(xx,yy));
}
return ans;
}//滿足黑點要求的最晚時間,左上第一個黑點出現
int white(int x,int y)
{
int bb[5];
for(int i=0;i<=3;i++)
{
int xx=x+dx[i],yy=y+dy[i];
if(xx>=1&&xx<=n&&yy>=1&&yy<=m)bb[i]=num(xx,yy);
else bb[i]=zs+1;
}
sort(bb+0,bb+4);
return bb[1];
}//滿足白點要求的最早時間,第二個黑點出現
void js(int h,int l,int i)
{
val[i]=val[i-1];
if(white(h,l)<i)val[i]--;
if(black(h,l)>i)val[i]++;
for(int j=0;j<=3;j++)
{
int x=h+dx[j],y=l+dy[j];
if(x>=1&&x<=n&&y>=1&&y<=m)
{
if(white(x,y)==i&&i<num(x,y))val[i]++;
if(black(x,y)==i&&i>num(x,y))val[i]--;
}
}
}
3.交換:剛才的做法就是因為不好維護交換而被暴斃。所以考慮交換\(u\),\(v\)有什麼影響,一定只對四聯通有變化。最多改 \(10\) 個點。改這十個點有貢獻的時間,區間修改。所以用線段樹維護一下即可。
程式碼
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n,m,q,zs,kz[N],val[N],st[20];
#define bianhao(x,y) (x-1)*m+y
#define num(x,y) kz[bianhao(x,y)]
int dx[5]={-1,0,1,0},dy[5]={0,-1,0,1};
int black(int x,int y)
{
int ans=zs+1;
for(int i=0;i<=1;i++)
{
int xx=x+dx[i],yy=y+dy[i];
if(xx>=1&&xx<=n&&yy>=1&&yy<=m)ans=min(ans,num(xx,yy));
}
return ans;
}//滿足黑點要求的最晚時間
int white(int x,int y)
{
int bb[5];
for(int i=0;i<=3;i++)
{
int xx=x+dx[i],yy=y+dy[i];
if(xx>=1&&xx<=n&&yy>=1&&yy<=m)bb[i]=num(xx,yy);
else bb[i]=zs+1;
}
sort(bb+0,bb+4);
return bb[1];
}//滿足白點要求的最早時間
struct node
{
int h,l;
}a[N];
struct stu
{
int mn,cnt,lz;
}sh[N<<2];
void js(int h,int l,int i)
{
val[i]=val[i-1];
if(white(h,l)<i)val[i]--;//這個點變黑之前是滿足條件的白點
if(black(h,l)>i)val[i]++;//這個點變黑之後是滿足條件的黑點
for(int j=0;j<=3;j++)
{
int x=h+dx[j],y=l+dy[j];
if(x>=1&&x<=n&&y>=1&&y<=m)//一定要判邊界
{
if(white(x,y)==i&&i<num(x,y))val[i]++;//它是白點的第二個出現的黑點,所以從現在開始是一個滿足條件的白點
if(black(x,y)==i&&i>num(x,y))val[i]--;//它是黑點的第一個出現的左上角黑點,所以從現在開始不是一個滿足條件的黑點
}
}
}
void update(int x)
{
sh[x].cnt=0;//直接清空
sh[x].mn=min(sh[x<<1].mn,sh[x<<1|1].mn);
if(sh[x<<1].mn==sh[x].mn)sh[x].cnt+=sh[x<<1].cnt;
if(sh[x<<1|1].mn==sh[x].mn)sh[x].cnt+=sh[x<<1|1].cnt;
return ;
}
void build(int x,int l,int r)
{
if(l==r)
{
sh[x].mn=val[l],sh[x].cnt=1;//最小值能否為0?不能,因為無論如何最左上的黑點一定符合黑點條件
return;
}
int mid=(l+r)>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
update(x);
return ;
}
void pushdown(int x)
{
if(sh[x].lz==0)return;
sh[x<<1].lz+=sh[x].lz,sh[x<<1].mn+=sh[x].lz;
sh[x<<1|1].lz+=sh[x].lz,sh[x<<1|1].mn+=sh[x].lz;
sh[x].lz=0;
return;
}
void modify(int x,int l,int r,int lt,int rt,int val)
{
if(l>rt||r<lt||rt<lt)return;
if(l>=lt&&r<=rt)
{
sh[x].mn+=val;sh[x].lz+=val;
return ;
}
pushdown(x);
int mid=(l+r)>>1;
if(lt<=mid)modify(x<<1,l,mid,lt,rt,val);
if(rt>mid)modify(x<<1|1,mid+1,r,lt,rt,val);
update(x);
}
int main()
{
//ios::sync_with_stdio(false);
// cin.tie(0); cout.tie(0);
cin>>n>>m>>q;
zs=n*m;
for(int i=1;i<=zs;i++)
{
cin>>a[i].h>>a[i].l;
a[i].h+=1,a[i].l+=1;//下標從1開始
num(a[i].h,a[i].l)=i;//變黑的時間點
}
for(int i=1;i<=zs;i++)js(a[i].h,a[i].l,i);
build(1,1,zs);
for(int i=1;i<=q;i++)
{
int u,v,xx,cnt=0;cin>>u>>v;u+=1,v+=1;//不要忘記下標加一
if(u>v)swap(u,v);//確保u比v小
if(u==v){cout<<sh[1].cnt<<'\n';continue;}
st[++cnt]=u;
st[++cnt]=v;
for(int j=0;j<=3;j++)
{
int x=a[u].h+dx[j],y=a[u].l+dy[j];
if(x>=1&&x<=n&&y>=1&&y<=m)st[++cnt]=num(x,y);
x=a[v].h+dx[j],y=a[v].l+dy[j];
if(x>=1&&x<=n&&y>=1&&y<=m)st[++cnt]=num(x,y);
}
sort(st+1,st+cnt+1);
for(int j=1;j<=cnt;j++)
{
if(st[j]==st[j-1])continue;//去重不能計算兩遍?
if((xx=white(a[st[j]].h,a[st[j]].l))<st[j])//交換u,v對滿足條件的白點減少情況(不存在不算)
modify(1,1,zs,max(u,xx),min(v,st[j])-1,-1);//如果是v周圍的點,將v換成u,只會增加區間不會減少區間,所以只考慮u周圍的點。
//對u周圍的u,v限制區間的話點u的貢獻就是從u到st[j],v就是從v到st[j],但有可能不是由u,v限制的,所以取min,max
if((xx=black(a[st[j]].h,a[st[j]].l))>st[j])//交換u,v對滿足條件的白點減少情況(不存在不算)
modify(1,1,zs,max(u,st[j]),min(v,xx)-1,-1);//如果是u周圍的點,將u換成v,只會增加區間不會減少區間
//對v周圍的區間,v的貢獻是身體從st[j]到v,u的貢獻就是從st[j]到u,
}
swap(num(a[u].h,a[u].l),num(a[v].h,a[v].l));swap(a[u],a[v]);
for(int j=1;j<=cnt;j++)
{
if(st[j]==st[j-1])continue;
if((xx=white(a[st[j]].h,a[st[j]].l))<st[j])modify(1,1,zs,max(u,xx),min(v,st[j])-1,1);
if((xx=black(a[st[j]].h,a[st[j]].l))>st[j])modify(1,1,zs,max(u,st[j]),min(v,xx)-1,1);
}
cout<<sh[1].cnt<<'\n';
}
return 0;
}