1 条题解
-
0
自动搬运
来自洛谷,原作者为

liuzhangfeiabc
**搬运于
2025-08-24 22:05:03,当前版本为作者最后更新于2018-10-10 18:05:58,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
题目大意:给定一个n×m的矩阵,矩阵中不重不漏地分布着0~n×m-1的元素。称一个大小为s的连续子矩阵是好的当且仅当其中不重不漏地出现了0~s-1的元素。每次交换矩阵中的两个元素,查询当前有多少个好的子矩阵。强制在线。
这题容易给人一种奇技淫巧的数据结构的感觉。那么到底该用什么数据结构,维护什么信息?
我们发现我们可以通过维护前i个元素在矩阵中位置分布相关的信息,来检验前i个元素是否能组成合法子矩阵。
开一棵线段树,其下标为i的位置维护的是前i个元素的信息。
那么问题来了,我们需要维护什么信息,能够支持修改和快速计算答案?
这确实是一个棘手的问题。估计ioi现场应该很多人都跪在这一步了。
我一开始的想法是,维护前i个元素所组成的图形的周长。后来发现据此并不能直接计算答案(一个大小一定的矩形其周长也可能有多种可能),遂放弃。
后来我想到一种“看似可行”的思路:维护前i个元素的x、y坐标的最小值和最大值,这样前i个元素组成合法子矩形当且仅当(xmax - xmin + 1) × (ymax - ymin + 1) = s。
这样我们将每个位置的值设为(xmax - xmin + 1) × (ymax - ymin + 1) - i,这个数一定非负,当且仅当它为0时答案合法。
然而这个想法在修改时遇到了麻烦:交换两个元素,可能导致一个区间的max和min值发生大规模变化,尤其是当把原来的最值移动到后面时,可能造成区间内最值的多次改变,我们可能面临每次操作O(n)次修改。
目前我还没有想到合适的方法来维护这个东西,如果有大神会维护的话麻烦在评论中留言谢谢。
那怎么办?我们有一个绝妙的思路:通过维护“特殊点”的信息来实现快速修改和判断合法性的目的!
为了方便,我们现在对于一个位置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,我们可以用类似维护矩形面积并的方式,记录区间最小值和最小值的个数。
于是我们就成功地用九省省选难度的数据结构解决了这道难题!
#include<bits/stdc++.h> using namespace std; #define gc getchar() #define pc putchar #define li long long li read(){ li x = 0,y = 0,c = gc; while(!isdigit(c)) y = c,c = gc; while(isdigit(c)) x = (x << 1) + (x << 3) + (c ^ '0'),c = gc; return y == '-' ? -x : x; } void print(li q){ if(q < 0) pc('-'),q = -q; if(q >= 10) print(q / 10); pc(q % 10 + '0'); } 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; int main(){ int i,j,u,v; n = read();m = read();k = read();p = n * m; for(i = 1;i <= p;++i){ a[i].x = read() + 1;a[i].y = read() + 1;mt(ax(i),ay(i)) = i; } for(i = 1;i <= p;++i){ vl[i] = vl[i - 1]; if(bg(i) < i) --vl[i]; if(ed(i) > i) ++vl[i]; for(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); for(i = 1;i <= k;++i){ u = read() + 1;v = read() + 1;if(u > v) swap(u,v); if(u == v){ print(s[1]);pc('\n');continue; } ft = 0; st[++ft] = u;st[++ft] = v; for(j = 0;j < 4;++j) if(chk(fx(ax(u),j),fy(ay(u),j))) st[++ft] = qwq(u,j); for(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(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(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); } print(s[1]);pc('\n'); } return 0; }
- 1
信息
- ID
- 3929
- 时间
- 3000ms
- 内存
- 262MiB
- 难度
- 7
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者