P11454 [USACO24DEC] 2D Conveyer Belt S

Luogu 题目传送门
USACO 题目传送门

这是我的一个悲惨的做题故事,在考试时没发现问题就错了两个测试点 T_T。
算法: 两次 \(dfs\)

\(\;\)

Solution:
我们先 \(dfs\) 一次把最后的结果给算好。再一个一个移除点,然后把每一个移除的点标记为任意传送带,如果移除后的点从不行 ( \(0\) ) 到了行 ( \(1\) ),检查附近指向这个点的传送带。然后记录被改变点的数量,以此类推。

拿第一个样例。
输入完后的结果因该是:

R L ?
U ? ?
? D L

不知道怎么消除那个加粗表格。现在我们的结果是 \(3\) 个不可行,然后删除最后一个输入 (2 1 U),改成 \(?\)

R L ?
? ? ?
? D L

发现这个点从不行到可行了,把结果减一。再 \(dfs\) 上下左右不可行且指这个点的点。操作完后的结果为 \(2\)。再删除倒数第二个输入 (1 2 L)

R ? ?
? ? ?
? D L

现在发现当前节点从不可行变可行了,把结果减一。\(dfs\) 上下左右,发现 (1, 1) 也变了,再把结果减一。操作完后的结果为 \(0\)。当结果为 \(0\) 时,后面就不用搜了,因为删掉元素后不会增多可行的数量。

real_ans 的最终结果 (从 \(0\)cnt ) 是 {3, 2, 0, 0, 0, 0, 0},所以输出时加个 cnt--

\(\;\)

Talk is cheap, **代码如下: **

#include<bits/stdc++.h>
using namespace std;
#define ll long long
bool vis[5002][5002],f[5002][5002];
char lst[5002][5002], direction;
struct node{
    int x,y;
}backward[200002];

ll n,Q,a,b,real_ans[200002],cnt,ans;
void dfs(int x,int y) {
    f[x][y]=1;
    if(x==0||y==0||x==n+1||y==n+1)return;
    if(vis[x][y]) return;
    else vis[x][y]=1;
    if(lst[x][y+1]=='L'||lst[x][y+1]=='?') dfs(x,y+1);
    if(lst[x][y-1]=='R'||lst[x][y-1]=='?') dfs(x,y-1);
    if(lst[x-1][y]=='D'||lst[x-1][y]=='?') dfs(x-1,y);
    if(lst[x+1][y]=='U'||lst[x+1][y]=='?') dfs(x+1,y);

}
void dfs2(int x,int y){
    if (x==0||y==0||x==n+1||y==n+1) {
        f[x][y]=1;
        return;
    }
    if(y<n&&((lst[x][y+1]=='L'||lst[x][y+1]=='?')&&f[x][y+1]==0)){
        f[x][y+1]=1;
        ans--;
        dfs2(x,y+1);
    }
    if(y>1&&((lst[x][y-1]=='R'||lst[x][y-1]=='?')&&f[x][y-1]==0)){
        f[x][y-1]=1;
        ans--;
        dfs2(x,y-1);
    }
    if(x>1&&((lst[x-1][y]=='D'||lst[x-1][y]=='?')&&f[x-1][y]==0)){
        f[x-1][y]=1;
        ans--;
        dfs2(x-1,y);
    }
    if(x<n&&((lst[x+1][y]=='U'||lst[x+1][y]=='?')&&f[x+1][y]==0)){
        f[x+1][y]=1;
        ans--;
        dfs2(x+1,y);
    }
}

int main(){
//    freopen("cin.in","r",stdin);
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); // 加输入/输出的速度
    //输入+初始化
    cin>>n>>Q;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            lst[i][j]='?';

    memset(vis,0,sizeof(vis));
    memset(f,0,sizeof(f));
    for (int i=Q;i>=1;i--) {
        cin>>a>>b>>direction;
        backward[i].x=a;
        backward[i].y=b;
        lst[a][b]=direction;
    }
	for (int i=1;i<=n;i++){ // 把边界都设为可行
		f[0][i]=1;
		f[n+1][i]=1;
		f[i][0]=1;
		f[n+1][i]=1;
	}

    
    // 一次dfs算完全部
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(!vis[i][j]){
                if(j==1&&(lst[i][j]=='?'||lst[i][j]=='L')) dfs(i,j);
                if(j==n&&(lst[i][j]=='?'||lst[i][j]=='R')) dfs(i,j);
                if(i==n&&(lst[i][j]=='?'||lst[i][j]=='D')) dfs(i,j);
                if(i==1&&(lst[i][j]=='?'||lst[i][j]=='U')) dfs(i,j);
            }

    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++) if(!f[i][j]) ans++;

    

    // 核心代码!!! 反过来算
    real_ans[cnt++]=ans;
    for (int i=1;i<=Q;i++){
        if (cnt==0){
            real_ans[cnt++]=0;
            continue;
        }
        int tmp1=backward[i].x,tmp2=backward[i].y;
        lst[tmp1][tmp2]='?';
        if (f[tmp1][tmp2]==0){
            if     (f[tmp1][tmp2+1]==1) f[tmp1][tmp2]=1;
            else if(f[tmp1][tmp2-1]==1) f[tmp1][tmp2]=1;
            else if(f[tmp1-1][tmp2]==1) f[tmp1][tmp2]=1;
            else if(f[tmp1+1][tmp2]==1) f[tmp1][tmp2]=1;
            else if(tmp1==0||tmp2==0||tmp1==n||tmp2==n) f[tmp1][tmp2]=1;
            if (f[tmp1][tmp2]==1) {
                ans--;
                dfs2(tmp1,tmp2);
            }
        }
        real_ans[cnt++]=ans;
    }
    
    // 我们多加了一次,把这个减回去
    cnt--;
    while (cnt--) cout<<real_ans[cnt]<<endl;
    return 0;
}

posted @ 2025-12-04 13:13  ProJon  阅读(0)  评论(0)    收藏  举报