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; }