2022-2023 ICPC, NERC, Northern Eurasia D - Dominoes 匈牙利 | 二分图匹配

考虑黑白染色,i+j%2==1的为黑格

一块多米诺就要覆盖一黑一白,相当于一个匹配

那全覆盖也就是存在完美匹配

(应该是网络流经典的trick?

讨论选取的点来自同一边和两边

1.同一边

任意挑选两个点去掉后左右两边个数不相等,肯定不存在完美匹配

贡献是cnt*(cnt-1)/2*2=cnt*(cnt-1)

2.不同边

考虑枚举删除左部点u

假设删掉的右部点v和u之间有增广路径可以到达,那么删除u和v之后,匹配数减少1,但仍存在完美匹配

如图

 

 假设我们枚举的左部点u为1号点,删掉6号的话,(2,3)、(4,5)都可以组成新的匹配,匹配数只会减少1,点数也会减少1,完美匹配仍存在

2号点和6号点同理

所以我们要删掉的点就是走增广路走不到的右部点(这个东西还有一个专门的名字,叫最大匹配必经点,如果数据范围再大一点就要用网络流了)

实现的话对每个左部点dfs一遍,走增广路相当于不断地dfs(match[to])

还有一个问题,为什么这样不会超时?

题目里面对答案和1e6取min保证了这点

假设左部点有n个,当n*(n-1)>1e6时,答案就是1e6,相当于说n最多只有1000

每个点连边最多只有4条

匈牙利的时间复杂度是O(V*E),为n*n*4

找走增广路走不到的右部点时,有n个点,最多走n步(把右边所有点都遍历一遍),为O(N²)

by the way:希望自己不要再炸细节了

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=4005;
int dx[5]={0,0,0,-1,1},dy[5]={0,-1,1,0,0};
ll n,m;
char mp[maxn][maxn];
bool valid(int x,int y){
    if(x<1||x>n||y<1||y>m) return false;
    if(mp[x][y]=='#') return false;
    return true;
}
set<int>ans[maxn];
vector<int>vec[maxn];
int match[maxn];
int lef[maxn],rig[maxn],id[maxn][maxn];
// int match[maxn]*[maxn] ?
int vis[maxn];
int dfs(int x){
    //if(x%2==0) s.push(x);
    for(int i=0;i<vec[x].size();i++){
        int to=vec[x][i];
        if(vis[to]) continue;
        vis[to]=1;
        if(!match[to]||dfs(match[to])){
            match[to]=x;
            match[x]=to;
        /*    while(!s.empty()){
                int y=s.top();s.pop();
                ans[x].insert(y);
            }
        */
            return 1;
        
        }
    }
    //if(s.top()==x) s.pop();
    //s.pop();
    return 0;
}
void mark(int u,int fa){
    for(int i=0;i<vec[u].size();i++){
        int to=vec[u][i];
        if(vis[to]) continue;
        vis[to]=1;
        ans[fa].insert(to);
        mark(match[to],fa);
    }
}
int main(){
    //freopen("lys.in","r",stdin);
    cin>>n>>m;
    ll c1=0,c2=0,ct=0;
    for(int _=1;_<=n;_++)
     for(int __=1;__<=m;__++){
         cin>>mp[_][__];
         if(mp[_][__]=='#') continue;
         else {
             ct++;
         }
     }
    ct/=2;
    if((ct*(ct-1))>=1e6){
        cout<<1000000;return 0;
    }
    ct=0;
    for(int i=1;i<=n;i++)
     for(int j=1;j<=m;j++){
         if(mp[i][j]=='#') continue;
         else {
             ct++;
             if((i+j)%2==0) {
                 lef[++c1]=ct;
             }
            else rig[++c2]=ct;
            id[i][j]=ct;
         }
     }
    // (i+j)%2==1 -> black 
    for(int i=1;i<=n;i++)
     for(int j=1;j<=m;j++){
          if(mp[i][j]=='#') continue;
         for(int k=1;k<=4;k++){
             int tx=i+dx[k],ty=j+dy[k];
             if(valid(tx,ty))  vec[id[i][j]].push_back(id[tx][ty]);    
         }
     }
    
    int matched=0;
    for(int i=1;i<=c1;i++){
        memset(vis,0,sizeof(vis));
         int add=dfs(lef[i]);
        matched+=add;
     }
    ll out=matched*(matched-1); 
    for(int i=1;i<=c1;i++){
        memset(vis,0,sizeof(vis));
        mark(lef[i],lef[i]);
        out+=matched-ans[lef[i]].size();
     }

     cout<<min(out,1ll*1000000)<<endl;
}

 

posted @ 2023-02-08 12:06  liyishui  阅读(62)  评论(1编辑  收藏  举报