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

seac_blue
Cogito ergo sum.搬运于
2025-08-24 22:31:46,当前版本为作者最后更新于2021-11-18 21:23:20,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
思路
逆序存储
顺序存储只能得到 60 分,原因在于浪费了太多的时间在无用的
PAINT函数中。考虑如下片段:SAVE PAINT ... ... PAINT ... LOAD 1此时
LOAD函数会使得很多PAINT函数被清除,但是朴素的算法会把这些函数全部都执行一遍,浪费了很多时间。因此,我们需要判断哪些PAINT函数是真正被调用的。但是从前往后扫描有可能无法得出正确的顺序。考虑如下片段(从隔壁题解看到的):
PAINT ... SAVE *PAINT ... SAVE LOAD 1 LOAD 2带 号的
PAINT函数实际上是被调用的。但是如果只是单纯地删除,这个函数就会被认为是无效的,从而得到错误的答案。此时我们就需要从后往前判断
PAINT函数真正的执行顺序。这同时启发我们,不仅可以找到PAINT函数的调用顺序,或许我们还可以用另外一种角度去调用这些函数。调用
PAINT函数的另外一种方案回到曾经写过的一道题:铺地毯。在这道题中,我们按照顺序在平面上覆盖矩形,最后询问某点上最后一个覆盖的矩形编号。
然而,这道题还有另外一种做法:从最后覆盖平面的矩形开始,倒序查找。只要找到某个矩形 覆盖了该点,则 必定是该点上最后一个覆盖的矩形。
这样做的正确性显然:后盖上的矩形必定在先盖上的矩形之上。所以只要 覆盖了点 , 之前的矩形就不可能是最后一个覆盖的矩形。
我们再考虑如下问题:如何判断每一个点上最后一个覆盖的矩形编号?
相信你也已经得到了答案:倒序查找每一个矩形,在其范围内覆盖没有被覆盖过的点。正确性也显然。
回到 SLIKA 这道题。既然可以倒序调用
PAINT函数,那么我们就模仿着铺地毯的方式去覆盖这个画布。显然我们能写出如下的伪代码片段:
倒序遍历操作: 如果操作为 LOAD: 返回到对应的 SAVE 处 如果操作为 PAINT: 遍历所有能绘制的位置: 如果这个格子颜色为 1 (没有上过色): 给这个格子上色但是我们很快就会发现:到了后面,每一个
PAINT函数都会经过很多次上过色的格子,从而浪费了很多的时间。显然这个方法还需要改进。每个格子最多只会被修改一次
我们考虑用这样的方式来优化:对于每一个格子,记录它之后(包括它本身)的第一个还可以被上色的格子。这样我们就可以像链表一样修改这些格子,只需要同时修改这些格子的后继就可以了。完全可以实现每个格子最多只会被修改一次。
你可能已经想到了一个方法:并查集。我们使用并查集来满足如上的要求。修改后继对应的就是并查集中的
union函数;查找下一个可修改的格子对应的就是find函数。存在的问题被完美解决了。以行为单位建立并查集,每一次绘制图形之后,都需要执行
union(x,x+2)的操作(绘制是每两格执行一次的),查找后继直接使用find(x)即可。值得提出的是,
union(x,x+2)当中, 的取值有可能就是 ,程序中为了防止数组越界或造成死循环,将并查集的范围稍稍调大了一点。当然,你也可以尝试着写一下特判,判断当前的union操作是否合法。代码
代码省略了头文件。个人的习惯是下标 +1。
typedef long long ll; ll read(){ char c=getchar();ll d=0,f=1; while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0' && c<='9'){d=(d<<3)+(d<<1)+(c^48);c=getchar();} return d*f; } const ll MAXM = 1e5+10; const ll MAXN = 1e3+10; ll n,k,m; struct Oper{ ll op; ll a1,a2,a3,a4,a5; }a[MAXM]; ll slot[MAXM];// 存档 ll slots;// 存档数量 ll fa[MAXN][MAXN];// 并查集 ll p[MAXN][MAXN];// 颜色 ll find(ll id,ll x){// 此处的 id 和 unionn 函数的一样,都指明了数组的第一维编号 if(fa[id][x]!=x){ fa[id][x]=find(id,fa[id][x]); } return fa[id][x]; } void unionn(ll id,ll x,ll y){ x=find(id,x); y=find(id,y); fa[id][x]=y; } int main(){ n=read();k=read();m=read(); for(ll i=1;i<=n;++i){ for(ll j=1;j<=n+2;++j){ fa[i][j]=j; p[i][j]=1; } } for(ll i=1;i<=m;++i){ string s;cin>>s; if(s=="PAINT"){ a[i].op=1; a[i].a1=read(); a[i].a2=read()+1; a[i].a3=read()+1; a[i].a4=read()+1; a[i].a5=read()+1; }if(s=="SAVE"){ a[i].op=2; slot[++slots]=i; }if(s=="LOAD"){ a[i].op=3; a[i].a1=read(); } } for(ll i=m;i>=1;--i){ if(a[i].op==3)i=slot[a[i].a1];// 退回对应的存档 if(a[i].op==1){ ll ybegin=a[i].a3; for(ll j=a[i].a2,ptr=0;j<=a[i].a4;++j,ptr=!ptr){ for(ll k=find(j,ybegin);k<=a[i].a5;k=find(j,k)){// 跳至其后继 p[j][k]=a[i].a1; unionn(j,k,k+2);// 调整后继 } if(!ptr)ybegin=a[i].a3+1;// 模拟棋盘状 else ybegin=a[i].a3; } } } for(ll i=1;i<=n;++i,putchar('\n')){ for(ll j=1;j<=n;++j){ printf("%lld ",p[i][j]); } } return 0; }祝您 AC 愉快。
- 1
信息
- ID
- 6844
- 时间
- 1000ms
- 内存
- 128MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者