2023NOIP A层联测17-爆炸

给出一个 n × m n×m n×m 的网格图,其中一些格子上有一个炸弹,一些格子上有一个水晶,剩下的格子为空地,什么也没有。

炸弹可以被引爆。每当一个炸弹被引爆,它必须选择:引爆它所在的这一行或者引爆它所在的这一列。水晶被引爆后会消失(即如果一个水晶所在的行列均被引爆,也只计算一次),炸弹被引爆后,会产生连锁反应,被引爆的炸弹也会选择引爆它所在的行或者列,如此下去直到没有新的炸弹被引爆。

现在你可以选择一个炸弹和其引爆的方向,请你求出最多可以引爆多少个水晶。

n , m ≤ 3000 n,m\le3000 n,m3000


当一个炸弹以一个方向被引爆时,被触发的炸弹引爆方向是相反的。显然这是最佳的。

考虑对每个炸弹的上下左右离它最近的炸弹连边。这样就形成了若干个连通块,互不影响,可以分别计算答案。

若一个连通块内有环,那么任意引爆其中一个炸弹,整个连通块的炸弹都会被引爆,得到连通块涉及的行列的水晶。

若没有环,就可以选择一个只有左右/上下有炸弹的炸弹,将其引爆,这样可以把连通块内所有炸弹引爆,对比上一个情况的水晶,容易发现少了某一列/行的水晶(建议自己手模一下),减去即可。

时间复杂度 O ( n m ) O(nm) O(nm)

具体实现看代码

#include
using namespace std;
const int N=3001,NN=9000001;
int n,m,num1,num2,ans,vis[NN],dfn[NN],low[NN],num,fl,d[NN];
char a[N][N];
int head[NN],nxt[NN<<2],to[NN<<2],cnt,w[NN<<2];
vector<int> v1,v2,v3;
unordered_map<int,int> m1,m2; 
void add(int u,int v,int W)
{
    to[++cnt]=v;
    w[cnt]=W;
    nxt[cnt]=head[u];
    head[u]=cnt;
}
int ID(int x,int y){return (x-1)*m+y;}
pair<int,int> DI(int x){return make_pair((x-1)/m+1,(x-1)%m+1);}
void dfs(int u,int fa)
{
    v3.push_back(u);
    auto czn=DI(u);
    vis[u]=1;
    if(!m1.count(czn.first)) m1[czn.first]=1,v1.push_back(czn.first);
    if(!m2.count(czn.second)) m2[czn.second]=1,v2.push_back(czn.second);
    for(int i=head[u];i;i=nxt[i]){
        if(to[i]!=fa){
            if(vis[to[i]]){fl=1;continue;}
            dfs(to[i],u);
        }
    }
}
bool check(int x)
{
    int x1=0,x2=0;
    for(int i=head[x];i;i=nxt[i]){
        if(w[i]) x1=1;
        else x2=1;
    }
    return x1^x2;
}
int main()
{
    freopen("boom.in","r",stdin);
    freopen("boom.out","w",stdout);
    scanf("%d%d%d%d",&n,&m,&num1,&num2);
    for(int i=1;i<=n;i++) scanf("%s",a[i]+1);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(a[i][j]!='b') continue;
            int x=i+1,y=j;
            while(x<=n&&a[x][y]!='b') x++;
            if(x<=n){
                add(ID(i,j),ID(x,y),1),add(ID(x,y),ID(i,j),1);
                d[ID(i,j)]++,d[ID(x,y)]++;
            }
            x=i,y=j+1;
            while(y<=m&&a[x][y]!='b') y++;
            if(y<=m){
                add(ID(i,j),ID(x,y),0),add(ID(x,y),ID(i,j),0);
                d[ID(i,j)]++,d[ID(x,y)]++;
            }
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(!vis[ID(i,j)]&&a[i][j]=='b'){
                fl=0;
                v1.clear(),v2.clear(),v3.clear();
                m1.clear(),m2.clear();
                dfs(ID(i,j),0);
                sort(v1.begin(),v1.end());
                sort(v2.begin(),v2.end());
                int sum=0;
                for(auto i:v1) for(int j=1;j<=m;j++) sum+=(a[i][j]=='k');
                for(int i=1;i<=n;i++) for(auto j:v2) sum+=(a[i][j]=='k');
                for(auto i:v1) for(auto j:v2) sum-=(a[i][j]=='k');
                if(fl){
                    ans=max(ans,sum);
                }
                else{
                    for(auto i:v3){
                        if(check(i)){
                            auto czn=DI(i);
                            int x=sum;
                            if(w[head[i]]){
                                for(int j=1;j<=m;j++) x-=(a[czn.first][j]=='k');
                                for(auto j:v2) x+=(a[czn.first][j]=='k');
                            }
                            else{
                                for(int i=1;i<=n;i++) x-=(a[i][czn.second]=='k');
                                for(auto i:v1) x+=(a[i][czn.second]=='k');
                            }
                            ans=max(ans,x);
                        }
                    }
                    if(d[ID(i,j)]==0){
                        auto czn=DI(ID(i,j));
                        int x=sum;
                        for(int j=1;j<=m;j++) x-=(a[czn.first][j]=='k');
                        for(auto j:v2) x+=(a[czn.first][j]=='k');
                        ans=max(ans,x);
                        x=sum;
                        for(int i=1;i<=n;i++) x-=(a[i][czn.second]=='k');
                        for(auto i:v1) x+=(a[i][czn.second]=='k');
                        ans=max(ans,x);
                    }
                }
            }
        }
    }
    cout<<ans;
}

你可能感兴趣的:(图论)