题解:P4898 [IOI 2018] seats 排座位

· · 题解

题意

给定一个 H\times W 的格点的一个排列,Q 次询问,每次交换两个元素,查询有多少个前缀点的集合恰是一个长方形(内部的点)。

## 题解 设 $N=HW$。 先考虑一维的情况怎么做。这是经典的点减边容斥:一个集合是一个区间当且仅当其点数减去边(即,相邻的点对)数等于 $1$。并且若集合非空,这个值一定大于等于 $1$。(具体的,等于连通块数,不过在这个题里不重要)因此使用线段树维护每个前缀的该值的最小值和,修改就是对涉及设计点相邻的 $O(1)$ 个点、边,做后缀加、减即可。时间复杂度 $O(N+Q\log N)$。 考虑是否能扩展类似的容斥。对于点集 $S$(假定 $S$ 非空),记 $f(\cdot)$ 表示要求其中 $\texttt x$ 对应位置存在而其它位置没有限制的匹配数量。例如,上面对于一维情况的讨论就是统计 $f(\texttt{x})-f(\texttt{xx})=1$ 的前缀数量。如果 $f$ 的参数是 $O(1)$ 大小的网格,那么 $f$ 的相关是好维护的:对于其在网格中的每一次出现,都取其对应位置在序列中所出现位置的最大值,这一后缀带来了对应的贡献。而修改也是类似的,只涉及 $O(1)$ 个 $f$ 的修改。因此,假如有一些 $f$ 的线性组合,满足只有长方形能取到最小值,那么整个题目就可以在 $O(N+Q\log N)$ 的时间复杂度内完成。 考虑如何刻画长方形的特点。首先,对于一个长方形,恰有一个最左上角的点。如果用 $f$ 来刻画,稍微加强一下条件,就是说恰有一个点在无法向左或向上走(而且对于任意点集都至少有这么一个点)。使用容斥,可以得到条件是 $f(\texttt{x})-f(\texttt{xx})-f(\begin{matrix}\texttt x\\\texttt{x}\end{matrix})+f(\begin{matrix}\texttt { x}\\\texttt{xx}\end{matrix})=1$。类似的,对于四个方向都有这个条件。 分析一下这个条件意味着什么。对于每个可以向左或向上走的格点,通过不断走,一定可以走到唯一的这一个不能再走的点。不难推出四个角的点一定是一个长方形的顶点。那么从顶点往相邻的顶点一定存在路径,也就是这个长方形的边框是完整的。但是也就仅此而已了。 考虑还有什么长方形满足的条件,但是空心长方形不满足。可以发现,对于每个 $2\times 2$ 的网格,如果其一条对角线上都在长方形中,那么整个 $2\times 2$ 的部分都在长方形中。而且加上这个条件就可以保证是长方形!这是因为,从边框出发,可以一行一行向下证明,每一行都是满的。 综上所述,对于任何点集 $S$,有: $$ \begin{cases} f(\texttt{x})-f(\texttt{xx})-f(\begin{matrix}\texttt x\\\texttt{x}\end{matrix})+f(\begin{matrix}\texttt { x}\\\texttt{xx}\end{matrix})\ge 1\\ f(\texttt{x})-f(\texttt{xx})-f(\begin{matrix}\texttt x\\\texttt{x}\end{matrix})+f(\begin{matrix}\texttt {x }\\\texttt{xx}\end{matrix})\ge 1\\ f(\texttt{x})-f(\texttt{xx})-f(\begin{matrix}\texttt x\\\texttt{x}\end{matrix})+f(\begin{matrix}\texttt {xx}\\\texttt{x }\end{matrix})\ge 1\\ f(\texttt{x})-f(\texttt{xx})-f(\begin{matrix}\texttt x\\\texttt{x}\end{matrix})+f(\begin{matrix}\texttt {xx}\\\texttt{ x}\end{matrix})\ge 1\\ f(\begin{matrix}\texttt { x}\\\texttt{x }\end{matrix})-f(\begin{matrix}\texttt { x}\\\texttt{xx}\end{matrix})\ge 0\\ f(\begin{matrix}\texttt {x }\\\texttt{ x}\end{matrix})-f(\begin{matrix}\texttt {x }\\\texttt{xx}\end{matrix})\ge 0\\ f(\begin{matrix}\texttt { x}\\\texttt{x }\end{matrix})-f(\begin{matrix}\texttt {xx}\\\texttt{x }\end{matrix})\ge 0\\ f(\begin{matrix}\texttt {x }\\\texttt{ x}\end{matrix})-f(\begin{matrix}\texttt {xx}\\\texttt{ x}\end{matrix})\ge 0\\ \end{cases} $$ 且恰在 $S$ 为长方形时全部取等。把它们全都加起来并除以二可得 $$ 2f(\texttt{x})-2f(\texttt{xx})-2f(\begin{matrix}\texttt x\\\texttt{x}\end{matrix})+f(\begin{matrix}\texttt { x}\\\texttt{x }\end{matrix})+f(\begin{matrix}\texttt {x }\\\texttt{ x}\end{matrix})\ge 2 $$ 且恰在 $S$ 为长方形时全部取等。按照前文所述维护即可。 ## 代码 ```cpp #include <bits/stdc++.h> #define rep(i,n) for(int i=0,del##i##verme=int(n);i<del##i##verme;++i) #define rep1(i,n) for(int i=1,parano##i##a=int(n);i<=parano##i##a;++i) #define pb push_back using namespace std; struct segtree{ int ad[2222222],mn[2222222],cnt[2222222]; void build(int i,int l,int r,int *c) { ad[i]=0; if(l==r) { mn[i]=c[l];cnt[i]=1; } else { int m=(l+r)>>1; build(i<<1,l,m,c);build(i<<1|1,m+1,r,c); if(mn[i<<1]==mn[i<<1|1]){mn[i]=mn[i<<1];cnt[i]=cnt[i<<1]+cnt[i<<1|1];} else if(mn[i<<1]<mn[i<<1|1]){mn[i]=mn[i<<1];cnt[i]=cnt[i<<1];} else {mn[i]=mn[i<<1|1];cnt[i]=cnt[i<<1|1];} } } void add(int i,int il,int ir,int ql,int qr,int dt) { if(il>qr||ql>ir) return ; if(ql<=il&&ir<=qr) { ad[i]+=dt;mn[i]+=dt;return ; } int m=(il+ir)>>1; add(i<<1,il,m,ql,qr,dt);add(i<<1|1,m+1,ir,ql,qr,dt); if(mn[i<<1]==mn[i<<1|1]){mn[i]=mn[i<<1];cnt[i]=cnt[i<<1]+cnt[i<<1|1];} else if(mn[i<<1]<mn[i<<1|1]){mn[i]=mn[i<<1];cnt[i]=cnt[i<<1];} else {mn[i]=mn[i<<1|1];cnt[i]=cnt[i<<1|1];} mn[i]+=ad[i]; } int query() { return (mn[1]==2)?cnt[1]:0; } }; segtree seg; int h,w,q,a,b; vector<int> vc[1000005]; int s[1000005],d[1000005]; const int dx[8]={0,0,1,-1,1,1,-1,-1}; const int dy[8]={1,-1,0,0,1,-1,1,-1}; const int vl[8]={-2,-2,-2,-2,1,1,1,1}; vector<int> pot; void Clear(int x,int y) { rep(i,8) { int nx=x+dx[i],ny=y+dy[i]; if(nx>=0&&ny>=0&&nx<h&&ny<w) { int id=max(vc[x][y],vc[nx][ny]); if(id==1145141919) continue; pot.pb(id); d[id]-=vl[i]; } } vc[x][y]=1145141919; } void Set(int x,int y,int vt) { vc[x][y]=vt; rep(i,8) { int nx=x+dx[i],ny=y+dy[i]; if(nx>=0&&ny>=0&&nx<h&&ny<w) { int id=max(vc[x][y],vc[nx][ny]); if(id==1145141919) continue; pot.pb(id); d[id]+=vl[i]; } } } void Solve() { for(int x:pot) { if(d[x]) { seg.add(1,1,h*w,x,h*w,d[x]); d[x]=0; } } pot.clear(); } int x[1000005],y[1000005]; int main() { ios_base::sync_with_stdio(false);cin.tie(0); cin>>h>>w>>q; rep(i,h) vc[i]=vector<int>(w); rep1(i,h*w) { cin>>x[i]>>y[i];vc[x[i]][y[i]]=i; } rep(i,h) rep(j,w) { if(i) ----s[max(vc[i][j],vc[i-1][j])]; if(j) ----s[max(vc[i][j],vc[i][j-1])]; if(i&&j) ++s[max(vc[i][j],vc[i-1][j-1])],++s[max(vc[i-1][j],vc[i][j-1])]; } rep1(i,h*w) s[i]+=s[i-1]+2; seg.build(1,1,h*w,s); while(q--) { cin>>a>>b;++a;++b;swap(x[a],x[b]);swap(y[a],y[b]); Clear(x[a],y[a]);Clear(x[b],y[b]); Set(x[a],y[a],a);Set(x[b],y[b],b); Solve(); cout<<seg.query()<<'\n'; } return 0; } ```