传送门:https://loj.ac/problem/2864
作死尝试这道全世界只有1人ac的神题。
先%一波dsgsjk,这位吊打全世界的julao可能是全世界第二个做出这题的现役oi选手。
题目大意:给定一个n*m的矩阵,矩阵中不重不漏地分布着0~n*m-1的元素。称一个大小为s的连续子矩阵是好的当且仅当其中不重不漏地出现了0~s-1的元素。每次交换矩阵中的两个元素,查询当前有多少个好的子矩阵。强制在线。
这题容易给人一种奇技淫巧的数据结构的感觉。那么到底该用什么数据结构,维护什么信息?
我们发现我们可以通过维护前i个元素在矩阵中位置分布相关的信息,来检验前i个元素是否能组成合法子矩阵。
开一棵线段树,其下标为i的位置维护的是前i个元素的信息。
那么问题来了,我们需要维护什么信息,能够支持修改和快速计算答案?
这确实是一个棘手的问题。估计现场应该很多人都跪在这一步了。
我一开始的想法是,维护前i个元素所组成的图形的周长。后来发现据此并不能直接计算答案(一个大小一定的矩形其周长也可能有多种可能),遂放弃。
后来我想到一种“看似可行”的思路:维护前i个元素的x、y坐标的最小值和最大值,这样前i个元素组成合法子矩形当且仅当(xmax - xmin + 1) * (ymax - ymin + 1) = s。
这样我们将每个位置的值设为(xmax - xmin + 1) * (ymax - ymin + 1) - i,这个数一定非负,当且仅当它为0时答案合法。
然而这个想法在修改时遇到了麻烦:交换两个元素,可能导致一个区间的max和min值发生大规模变化,尤其是当把原来的最值移动到后面时,可能造成区间内最值的多次改变,我们可能面临每次操作O(n)次修改。
目前我还没有想到合适的方法来维护这个东西,如果有大神会维护的话麻烦在评论中留言谢谢。
那怎么办?就要祭出dsgsjk大神的天才级思路了:通过维护“特殊点”的信息来实现快速修改和判断合法性的目的!
为了方便,我们现在对于一个位置i,将前i个点染成黑色,后面的点为白色。
我们维护这么两个东西:
1、所有的黑点中,有多少个点的左侧和上侧都不挨着黑点(可能是白点或边缘)。
2、所有的白点中,有多少个点的周围四连通地挨着超过2个黑点。
这两个信息能说明什么?
首先,容易证明所有的合法子矩阵都满足:第一个信息为1,第二个信息为0。接下来我们证明:一个非法的图形不可能同时满足以上两个条件。
容易看出,对于每个黑点组成的四连通块,我们总能选出其最左上的点(至少一个,也可能一个连通块中有多个合法点),使得它的左边和上边都不是黑点。也就是说,黑色连通块数量<=信息1。
因此,一个有>=2个连通块的图形不可能满足信息1数量为1。
再来考虑一个连通块但不是矩形的图形。容易发现这个图形一定存在如下的“拐角”:
xxoo
xxoo
xxxx
xxxx
x表示黑点,o表示白点,2行3列那个白点就是“拐角”。而在拐角处的白点满足信息2。
也就是说,存在拐角的图形不可能满足信息2数量为0。
那么同时满足1和2的图形,就只有“一个连通块且不存在拐角”的图形了——也就是矩形!
这样我们就证明了条件的充要性。那么怎么维护呢?
我们直接维护两个信息的和(这个和显然是正整数),然后统计有多少个位置答案为1。
交换两个位置时,我们可以把预制相邻的所有位置都拿出来(最多不超过10个,注意去重),然后暴力地对每个位置,先扣去原来对信息的贡献,再加上操作后对信息的贡献。对应在线段树上就是O(1)次区间修改。
至于查询区间有多少个1,我们可以用类似维护矩形面积并的方式,记录区间最小值和最小值的个数。
额……或许这道令全世界oi高手望而却步的难题,被dsgsjk做成了一道九省难度的数据结构?
//以下代码根据ioi提交格式编写
#include<bits/stdc++.h>
using namespace std;
#include"seats.h"
int n,m,p,k,jz[1000010],vl[1000010];
struct wz{
int x,y;
}a[1000010];
int t[4000010],s[4000010],c[4000010];
#define ls q << 1
#define rs q << 1 | 1
#define ln ls,l,mid
#define rn rs,mid + 1,r
#define md int mid = l + r >> 1
void build(int q,int l,int r){
if(l == r){
t[q] = vl[l];s[q] = 1;return;
}
md;
build(ln);build(rn);
t[q] = min(t[ls],t[rs]);
s[q] = 0;if(t[q] == t[ls]) s[q] += s[ls];if(t[q] == t[rs]) s[q] += s[rs];
}
inline void ps(int q){
t[ls] += c[q];c[ls] += c[q];
t[rs] += c[q];c[rs] += c[q];
c[q] = 0;
}
void ins(int q,int l,int r,int al,int ar,int x){
if(al > ar) return;
if(l >= al && r <= ar){
t[q] += x;c[q] += x;return;
}
ps(q);md;
if(mid >= al) ins(ln,al,ar,x);
if(mid < ar) ins(rn,al,ar,x);
t[q] = min(t[ls],t[rs]);
s[q] = 0;if(t[q] == t[ls]) s[q] += s[ls];if(t[q] == t[rs]) s[q] += s[rs];
}
int dx[4] = {0,-1,0,1},dy[4] = {-1,0,1,0};
#define mt(x,y) jz[(x - 1) * m + (y)]
#define lx(x) (x + dx[0])
#define ux(x) (x + dx[1])
#define fx(x,i) (x + dx[i])
#define fy(x,i) (x + dy[i])
#define ly(x) (x + dy[0])
#define uy(x) (x + dy[1])
#define chk(x,y) (x >= 1 && x <= n && y >= 1 && y <= m)
#define ax(q) a[q].x
#define ay(q) a[q].y
#define qwq(x,y) mt(fx(ax(x),y),fy(ay(x),y))
inline int ed(int q){
int mn = p + 1;
if(chk(lx(ax(q)),ly(ay(q)))) mn = min(mn,qwq(q,0));
if(chk(ux(ax(q)),uy(ay(q)))) mn = min(mn,qwq(q,1));
return mn;
}
inline int bg(int q){
int mn1 = p + 1,mn2 = p + 1;
for(int i = 0;i < 4;++i) if(chk(fx(ax(q),i),fy(ay(q),i))){
if(qwq(q,i) < mn1) mn2 = mn1,mn1 = qwq(q,i);
else if(qwq(q,i) < mn2) mn2 = qwq(q,i);
}
return mn2;
}
int st[15],ft;
void give_initial_chart(int h,int c,vector<int> x,vector<int> y){
n = h;m = c;p = n * m;
for(int i = 1;i <= p;++i){
a[i].x = x[i - 1] + 1;a[i].y = y[i - 1] + 1;mt(ax(i),ay(i)) = i;
}
for(int i = 1;i <= p;++i){
vl[i] = vl[i - 1];
if(bg(i) < i) --vl[i];
if(ed(i) > i) ++vl[i];
for(int j = 0;j < 4;++j) if(chk(fx(ax(i),j),fy(ay(i),j))){
if(qwq(i,j) < i && ed(qwq(i,j)) == i) --vl[i];
else if(qwq(i,j) > i && bg(qwq(i,j)) == i) ++vl[i];
}
}
build(1,1,p);
}
int swap_seats(int u,int v){
++u;++v;if(u > v) swap(u,v);
if(u == v) return s[1];
ft = 0;
st[++ft] = u;st[++ft] = v;
for(int j = 0;j < 4;++j) if(chk(fx(ax(u),j),fy(ay(u),j))) st[++ft] = qwq(u,j);
for(int j = 0;j < 4;++j) if(chk(fx(ax(v),j),fy(ay(v),j))) st[++ft] = qwq(v,j);
sort(st + 1,st + ft + 1);
for(int j = 1;j <= ft;++j) if(st[j] != st[j - 1]){
if(bg(st[j]) < st[j]) ins(1,1,p,max(bg(st[j]),u),min(st[j],v) - 1,-1);
if(ed(st[j]) > st[j]) ins(1,1,p,max(st[j],u),min(ed(st[j]),v) - 1,-1);
}
swap(mt(ax(u),ay(u)),mt(ax(v),ay(v)));swap(a[u],a[v]);
for(int j = 1;j <= ft;++j) if(st[j] != st[j - 1]){
if(bg(st[j]) < st[j]) ins(1,1,p,max(bg(st[j]),u),min(st[j],v) - 1,1);
if(ed(st[j]) > st[j]) ins(1,1,p,max(st[j],u),min(ed(st[j]),v) - 1,1);
}
return s[1];
}