ZJNU 2663

ZJNU 2663 - SLIKA

题面

description

有一个(n imes n)的网格图,(k)种颜色,(m)次操作,棋盘初始时颜色均为(1)

操作分三种,第一种操作(PAINT)会选择一个矩形区域与一种颜色,要求按棋盘方式对选定的区域涂色,如上图所示;后涂的颜色会覆盖在先前涂的颜色上。

第二种操作(SAVE)会保存一个副本,递增编号。

第三种操作(LOAD)则会加载先前保存的副本。

问按顺序执行完所有操作后,网格最终的状态。


思路

忘了哪里有做到过一次,也是逆序还原操作来优化

至于利用并查集来跳过已使用的部分的话,印象里2020ICPC银川站的某道银冲金题(加点手速和少罚时)也是这个做法,算是比较常见的

按顺序进行,时间复杂度(O(mcdot n^2)),空间复杂度(O(mcdot n^2)),均不可行。

首先考虑(SAVE)(LOAD)操作,执行(LOAD)操作后,会跳转至先前的某一存档,也就说明从那个存档开始到当前的(LOAD)操作之间的所有(PAINT)在此时均被忽略掉了。

发现按顺序进行(PAINT)操作时,后面进行的操作的涂色会将先前的涂色给覆盖掉;也就是说如果逆序看所有的操作,产生位置冲突时后面的操作将会跳过该位置的涂色。

因此,如果我们能够快速将该跳过的位置直接跳过,最终涂色操作最多也就只会做(n^2)级别次,而不是按顺序执行所有操作时最坏情况下需要进行(mcdot n^2)级别次。

那么考虑快速跳过已经被涂色的位置,发现可以将每一行都单独看作一个并查集,如果我们只枚举待涂色区域的某一维(行),然后借助并查集跳过已经被涂色的位置(列),那么最终的时间复杂度实际上只有操作数与枚举数的乘积,即(O(mcdot n))

考虑实现,假设当前我们将位置((i,j))进行涂色,保证行独立处理((i)不变),因此向右的下一个涂色的位置应是((i,j+2))。定义(to[i][j])表示在位置((i,j))时,下一个待处理的(y)轴坐标,利用并查集维护。那么每次涂色之后执行的应当就是将(to[i][j])合并到(to[i][j+2])(注意向右合并)。合并后则直接让(y=find(x,to[x][y]))跳至下一个(y)坐标即可((find(x,y))函数即对于(x)行的查询),借助路径压缩优化过程。

最后考虑(SAVE)(LOAD),在倒序看所有操作时,每一次(LOAD)实际上就是跳过了与其对应的(SAVE)操作到它之间的所有(PAINT)操作(在那一次(SAVE)之后的所有(PAINT)都因为这一次(LOAD)无效了),所以该部分简单处理以下,其后直接跳转操作数即可。


#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define perr(i,a,b) for(int i=(a);i>(b);i--)
#define all(a) (a).begin(),(a).end()
#define mst(a,b) memset(a,b,sizeof(a))
#define pb push_back
using namespace std;
typedef long long ll;

int n,k,q;
struct node
{
    int op,clr,x1,y1,x2,y2;
}ar[100050];
int savepos[100050],loadto[100050],savecnt=0;
// PAINT SAVE LOAD

int to[1050][1050];
int fnd(int x,int a){
    return a==to[x][a]?a:(to[x][a]=fnd(x,to[x][a]));
}
void merge(int x,int a,int b){
    to[x][a]=fnd(x,to[x][b]);
}
int ans[1050][1050];

void solve()
{
    cin>>n>>k>>q;
    repp(i,0,n)
        repp(j,0,n)
            ans[i][j]=1;
            
    rep(i,1,q)
    {
        string s;
        cin>>s;
        if(s[0]=='P')
        {
            ar[i].op=1;
            cin>>ar[i].clr;
            cin>>ar[i].x1;
            cin>>ar[i].y1;
            cin>>ar[i].x2;
            cin>>ar[i].y2;
        }
        else if(s[0]=='S')
        {
            ar[i].op=2;
        }
        else
        {
            ar[i].op=3;
            cin>>ar[i].clr;
        }
    }
    rep(i,1,q)
    {
        if(ar[i].op==2)
            savepos[++savecnt]=i;
    }
    rep(i,1,q)
    {
        if(ar[i].op==3)
            loadto[i]=savepos[ar[i].clr];
    }
    repp(x,0,n)
        rep(y,0,n+1) //y会与y+2合并,注意边界情况
        {
            to[x][y]=y;
        }
    per(i,q,1)
    {
        if(ar[i].op==1)
        {
            rep(x,ar[i].x1,ar[i].x2)
            {
                int y=ar[i].y1+(x-ar[i].x1)%2; //起始y
                for(y=fnd(x,to[x][y]);y<=ar[i].y2;y=fnd(x,to[x][y]))
                {
                    ans[x][y]=ar[i].clr;
                    merge(x,y,y+2); //涂色后将y与y+2合并
                }
            }
        }
        else if(ar[i].op==2)
            continue;
        else
            i=loadto[i];
    }
    repp(i,0,n)
        repp(j,0,n)
            cout<<ans[i][j]<<(j==n-1?'
':' ');
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    solve();
    return 0;
}

原文地址:https://www.cnblogs.com/stelayuri/p/15185547.html