中石油训练赛 - 奎奎画画(思维+并查集+离线处理)

题目描述

“为你写诗,为你静止,为你做不可能的事”,爱情是一种怪事,它让奎奎开始学习画画。奎奎认为一张画的艺术价值等于画上的白色联通块个数(当一个格子和它上下左右四个方向上的某个相邻格子颜色相同,则认为它们属于同一个联通块),奎奎还认为他作画的艺术价值和妹子对他的好感度紧密相关,因此奎奎非常在意每一时刻他的画的艺术价值。 为了简化题目,奎奎在一张n行m列的白色矩形格子画布上作画,他一共画了q笔,每一笔都是从(x1,y1)格子开始到(x2,y2)格子结束(x1=x2或y1=y2),将所有满足x1<=x<=x2并且y1<=y<=y2的格子涂黑。奎奎想知道当他画完每一笔之后,这幅画的艺术价值是多少。

输入

第一行三个整数n,m,q (1≤n, m≤1000, 1≤q≤10000 )
下面q行每行4个整数x1,y1,x2,y2 (1≤x1≤x2≤n, 1≤y1≤y2≤m)描述奎奎画的q条线段的起点和终点

输出

q行,对于奎奎画的每一条线段,输出一行一个整数表示该线段画完之后画布上白色联通块的个数。

样例输入 Copy

4 6 5
2 2 2 6
1 3 4 3
2 5 3 5
4 6 4 6
1 6 4 6

样例输出 Copy

1
3
3
4
3

比赛的时候还剩二十分钟开始切这个题,读完题后的思维跳跃也是一波三折,队友先是提出二维差分维护(读错题了),被我一口回绝,因为题目要求强制在线,二维差分时间复杂度高达1e10,肯定不行,讨论了一小会无果,准备放弃,这时队友发现了关键点,题目给出的每次更新保证了是一个直线,(一开始我也读错题了,以为每次需要更新一个子矩阵。。),这个时候暴力维护的复杂度瞬间下降到了 1e7 ,感觉又变成了一道可以切的题,于是研究如何优化,虽然维护矩阵的复杂度到了1e7,但是维护答案的复杂度仍然是1e10,考虑强制在线的另一种做法就是离线处理,正难则反,不难想到倒着维护,在这个思路的引导向,逐渐想出了正解,也就是并查集维护

题目分析:其实想到并查集倒着维护就比较简单了,对于每条黑线,我们用maze[ i ][ j ]表示每个点被染成黑色的次数,在读入数据时,顺便将整个矩阵维护为添加了 q 条黑边后的状态,此时bfs或dfs找出所有白色的连通块,并用并查集维护(用搜索找连通块纯粹是因为好写),然后倒着遍历每一条黑色的边,将其还原为白色,对于一条直线上的某个点而言,如果这个点在删除掉当前黑线后,变为了白色,那么无非只有两种情况:

扫描二维码关注公众号,回复: 9826118 查看本文章
  1. 当前的点是新出现的一个白色连通块
  2. 当前的点和周围的白色连通块相连

分类讨论一下就好了,注意一下,如果当前点周围的白色连通块的数量等于一的话,那么当前点只是单纯的和这个白色连通块连接起来了,对答案没有影响,如果周围白色联通卡的数量大于一的话,那么就将其连到一起了,具体的看代码实现吧,这一点点逻辑把我绕了有小半个小时

代码:
 

#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
using namespace std;
        
typedef long long LL;
       
typedef unsigned long long ull;
        
const int inf=0x3f3f3f3f;
   
const int N=1e3+100;
 
const int b[4][2]={0,1,0,-1,1,0,-1,0};
 
int maze[N][N],n,m,k,ans[10100],f[N*N],sum=0;
 
bool vis[N][N];
 
int get_id(int x,int y)
{
    return (x-1)*m+y-1;
}
 
int find(int x)
{
    return x==f[x]?x:f[x]=find(f[x]);
}
 
bool merge(int x,int y)
{
    int xx=find(x);
    int yy=find(y);
    if(xx!=yy)
    {
        f[xx]=yy;
        return true;
    }
    return false;
}
 
void dfs(int x,int y,int sx,int sy)
{
    merge(get_id(x,y),get_id(sx,sy));
    vis[x][y]=true;
    for(int i=0;i<4;i++)
    {
        int xx=x+b[i][0];
        int yy=y+b[i][1];
        if(xx<=0||yy<=0||xx>n||yy>m)
            continue;
        if(vis[xx][yy]||maze[xx][yy])
            continue;
        dfs(xx,yy,sx,sy);
    }
}
 
struct Que
{
    int x1,x2,y1,y2;
    void input()
    {
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        for(int i=min(x1,x2);i!=max(x1,x2)+1;i++)
            for(int j=min(y1,y2);j!=max(y1,y2)+1;j++)
                maze[i][j]++;
    }
    void change()
    {
        for(int i=min(x1,x2);i!=max(x1,x2)+1;i++)
            for(int j=min(y1,y2);j!=max(y1,y2)+1;j++)
                maze[i][j]--;
    }
    void solve()
    {
        change();
        int res=0;//记录当前直线沟通了多少个连通块
        for(int i=min(x1,x2);i!=max(x1,x2)+1;i++)
            for(int j=min(y1,y2);j!=max(y1,y2)+1;j++)
            {
                if(maze[i][j])//删去后仍然为黑色 
                    continue;
                int cnt=0;//记录周围有多少个不同的白色连通块
                for(int k=0;k<4;k++)
                {
                    int xx=i+b[k][0];
                    int yy=j+b[k][1];
                    if(xx<=0||yy<=0||xx>n||yy>m)
                        continue;
                    if(maze[xx][yy])
                        continue;
                    if(merge(get_id(i,j),get_id(xx,yy)))
                        cnt++;
                }
                if(cnt==0)//新出现的单独连通块
                    sum++;
                else//否则记录沟通了多少个连通块
                    res+=cnt-1;
            }
        sum-=res;
    }
}q[10100];
 
void init()
{
    for(int i=0;i<=get_id(n,m);i++)
        f[i]=i;
}
  
int main()
{
#ifndef ONLINE_JUDGE
//  freopen("input.txt","r",stdin);
//  freopen("output.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);
    scanf("%d%d%d",&n,&m,&k);
    init();
    for(int i=1;i<=k;i++)
        q[i].input();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(!vis[i][j]&&!maze[i][j])//找白色连通块
            {
                dfs(i,j,i,j);
                sum++;
            }
    for(int i=k;i>=1;i--)//维护答案
    {
        ans[i]=sum;
        q[i].solve();
    }
    for(int i=1;i<=k;i++)
        printf("%d\n",ans[i]);
     
     
     
     
     
     
     
     
     
     
     
 
    return 0;
}
发布了691 篇原创文章 · 获赞 27 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_45458915/article/details/104748513
今日推荐