USACO24DEC 2D Conveyer Belt S [ 蓝 ] [ 图论建模 ] [ Flood Fill ]

2D Conveyer Belt:图论建模+洪水填充妙妙题。质量极高。

观察

首先面对这种不断往图里面加点或者边,且满足前面的答案可以由加边后推出的题,第一个想到的就得是倒序枚举加边过程的 trick。一个经典的例子就是并查集倒序枚举操作。

然后我们观察,什么形态的图才会导致陷入一个死循环中。显然,要一堆方格围起来形成一个封闭的区域。

那么我们判断一个点会不会陷入死循环的方式就出来了:

  • 若这个点还未确定方向,则与它四联通的点中只要有一个可以不陷入死循环,他就可以不陷入死循环。
  • 若这个点方向已确定,则只有它要去的点可以不陷入死循环,他才能不陷入死循环。

于是,当一个图形态确定时,我们可以通过泛洪算法来求出哪些点可以不陷入死循环中。实现上,我们从四个边界外出发,然后向内依次拓展,对于一个待拓展的点判断能否找到一个可以不让他陷入死循环的点,转移一下即可。

时间复杂度 \(O(n^2)\)

去除限制

接下来就是要考虑如何倒序枚举加边过程,即不断将图中本来确定方向的点换成问号。

显然,确定方向是一个比问号更强的限制。一旦去掉这个限制,陷入死循环的点一定不会增加。

同时,如果一个点本来就不会陷入死循环,那么我们就没有必要进行这次改成问号的操作了。因为这样一定最优了。

那么如果这个点本来会陷入死循环呢?我们依然可以按上述判断方法判断这个点能否不陷入死循环。若可以不陷入,那么就把这个点 Flood Fill 一下,枚举与它四联通的点进行拓展即可。

由于每个点不会被拓展后又变回去,所以每个点最多被拓展一次。那么均摊下来,整题时间复杂度就是 \(O(n^2)\) 的了。

代码

实现上,我们可以在 dfs 内部判断是否已被填充,是的话就不拓展了。然后在一个点新拓展的时候遍历与它四联通的点,在待拓展点的递归内部判断能否填充。

#include <bits/stdc++.h>
#define fi first
#define se second
#define lc (p<<1)
#define rc ((p<<1)|1)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pi;
int n,q,a[1005][1005],now=0;
int gox[]={0,0,1,-1};
int goy[]={1,-1,0,0};
int goc[]={2,1,4,3};
struct qry{
    int x,y,tp,ans;
}b[200005];
bitset<1005>vis[1005];
bool legal(int x,int y)
{
    return (x>=1&&x<=n&&y>=1&&y<=n);
}
void dfs(int x,int y)
{
    if(vis[x][y])return;
    bool flag=0;
    if(a[x][y])
    {
        int tx=x+gox[goc[a[x][y]-1]-1],ty=y+goy[goc[a[x][y]-1]-1];
        if(vis[tx][ty])flag=1;
    }
    else
    {
        for(int i=0;i<4;i++)
        {
            int tx=x+gox[i],ty=y+goy[i];
            if(vis[tx][ty])flag=1;
        }
    }
    if(!flag)return;
    now++;
    vis[x][y]=1;
    for(int i=0;i<4;i++)
    {
        int tx=x+gox[i],ty=y+goy[i];
        if(legal(tx,ty))dfs(tx,ty);
    }
}
void init()
{
    now=0;
    for(int i=1;i<=n;i++)
    {
        vis[i][0]=1;
        vis[0][i]=1;
        vis[i][n+1]=1;
        vis[n+1][i]=1;
    }
    for(int i=1;i<=n;i++)
    {
        dfs(i,1);
        dfs(i,n);
        dfs(1,i);
        dfs(n,i);
    }
}
int main()
{
    //freopen("sample.in","r",stdin);
    //freopen("sample.out","w",stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>q;
    for(int i=1;i<=q;i++)
    {
        int x,y,tp;
        char c;
        cin>>x>>y>>c;
        if(c=='L')tp=1;
        else if(c=='R')tp=2;
        else if(c=='U')tp=3;
        else tp=4;
        b[i]={x,y,tp,0};
        a[x][y]=tp;
    }
    init();
    for(int i=q;i>=2;i--)
    {
        b[i].ans=now;
        a[b[i].x][b[i].y]=0;
        dfs(b[i].x,b[i].y);
    }
    b[1].ans=now;
    for(int i=1;i<=q;i++)cout<<n*n-b[i].ans<<'\n';
    return 0;
}
posted @ 2024-12-24 00:45  KS_Fszha  阅读(6)  评论(0编辑  收藏  举报