1 条题解

  • 0
    @ 2026-4-30 15:24:25

    我会做,好耶。

    首先可以发现风向串并不重要,我们可以预处理出 fSf_S 表示所有风向都在集合 SS 内的最长连续段,接下来很容易就可以判断一个点是否会被周围的点传染。

    先不管计数。

    容易证明只要从点 xx 开始搜索可以传染到 yy,那么选择 yy 作为起点就一定不劣于 xx。此时,我们从 xxyy 连一条边,表示我们不需要求 xx 的答案,求 yy 就可以了。随着我们的搜索,整张图会形成一个森林。

    我们考虑每次从没有出度的点开始搜索以他为起点的传染情况,如果传染到了一个和自己不在同一个连通块里的点,那就可以直接连边(和指向的连通块合并)然后扔掉当前点。如果在同一个连通块里则要继续搜索,因为显然你再扔掉当前点就成环了,当前连通块内所有点都有出度,但你没算出答案。

    如果传染到最后都没能走出自己这个连通块,那么我们就算出了这个连通块的答案,算进总答案里,之后直接不考虑这个块即可。因为如果别的块指向这个块内的点,那个块整体都不优。

    假如我们从一个大小为 ss 的连通块开始搜索,在至多传染 ss 个位置之后,就要么传染不动,要么传染到了别的连通块并与之合并了,进行一次这样的搜索的时间复杂度是 O(s)O(s)。在进行足够多搜索之后,所有的连通块全都被删掉,也就被统计进答案里了。

    容易想到,如果我们每次从最小的连通块开始搜索,就可以保证每次合并都是小集合向大集合合并,时间复杂度是小集合的大小。这是启发式合并的过程,时间复杂度为 O(RClogRC)O(RC \log RC)

    然后考虑计数。我们只在传染不动的时候会统计答案,此时,因为我们是从一个连通块的根开始搜索,他能走到的所有点也都能走到他自己,换言之,他走到的点之间的可达关系构成一个 SCC。 显然一个点的可达点数在当前连通块内最小当且仅当他在这个 SCC 里面。所以这个连通块的可行点数数值上就是最小答案。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    int M,n,m;
    #define N 805
    char s[200005],t[10]="NSWE";
    int fs[16];
    inline int id(int x,int y){
        return (x-1)*m+y;
    }
    int fa[N*N],sz[N*N],a[N][N],vis[N][N],dead[N][N];
    int find(int x){
        return x==fa[x]?x:fa[x]=find(fa[x]);
    }
    priority_queue<pair<int,int> >pq;
    queue<pair<int,int> >q;
    vector<pair<int,int> >vec;
    int dx[4]={-1,1,0,0};
    int dy[4]={0,0,-1,1};
    int ans1=0x3f3f3f3f,ans2=0;
    bool check(int x,int y){
        if(!a[x][y])return false;
        if(vis[x][y])return false;
        int c=0;
        for(int d=0;d<4;d++)if(vis[x+dx[d]][y+dy[d]])c|=1<<d;
        return a[x][y]<=fs[c];
    }
    int main(){
        scanf("%d%d%d",&M,&n,&m);
        scanf("%s",s);
        for(int i=0;i<M;i++)s[i+M]=s[i];
        for(int j=0;j<16;j++){
            int cnt=0;
            for(int i=0;i<M*2;i++){
                int fl=0;
                for(int k=0;k<4;k++)if(((1<<k)&j) && t[k]==s[i])fl=1;
                if(fl)fs[j]=max(fs[j],++cnt);
                else cnt=0;
            }
            if(cnt==M*2)fs[j]=0x3f3f3f3f;
        }
        for(int i=1;i<=n*m;i++)fa[i]=i,sz[i]=1;
        for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)scanf("%d",&a[i][j]);
        for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)if(a[i][j])pq.push({-1,id(i,j)});
        while(!pq.empty()){
            auto [SZ,S]=pq.top();
            pq.pop();
            if(fa[S]!=S || sz[S]!=-SZ)continue;
            int sx=(S-1)/m+1,sy=(S-1)%m+1;
            vec.clear();
            vec.push_back({sx,sy});
            while(!q.empty())q.pop();
            q.push({sx,sy});
            vis[sx][sy]=1;
            int to=-1;
            while(!q.empty()){
                auto [x,y]=q.front();q.pop();
                for(int d=0;d<4;d++){
                    int nx=x+dx[d],ny=y+dy[d];
                    if(!check(nx,ny))continue;
                    int v=id(nx,ny);
                    if(find(v)!=S){
                        to=find(v);
                        break;
                    }
                    vis[nx][ny]=1;
                    vec.push_back({nx,ny});
                    q.push({nx,ny});
                }
                if(to!=-1)break;
            }
            for(auto [x,y]:vec)vis[x][y]=0;
            if(to==-1){
                dead[sx][sy]=1;
                if(vec.size()<ans1)ans1=ans2=vec.size();
                else if(vec.size()==ans1)ans2+=vec.size();
            }
            else{
                fa[S]=to;
                sz[to]+=sz[S];
                if(!dead[(to-1)/m+1][(to-1)%m+1])pq.push({-sz[to],to});
            }
            vec.clear();
        }
        printf("%d\n%d\n",ans1,ans2);
        return 0;
    }
    
    • 1

    信息

    ID
    10167
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者