HK+质因子分解 HK是二分图匹配中匈牙利算法的优化,时间复杂度O(sqrt(n)*m) 先通过bfs寻找多条增广路,记下每个点到源点的距离(类似于网络流dinic算法),然后用类似于匈牙利算法中dfs的方法,进行匹配。 要求图是二分的,并且根据增广路的特性 模板:
#include<bits/stdc++.h> #define ll long long using namespace std; const int N =5e5+5; int t,n,n1,n2;//n1:左边的点数,n2:右边的点数 vector<int> g[N];//存图 int mx[N], my[N];//用来记左边点匹配的点,右边点匹配的点 queue<int> que;//bfs int dx[N],dy[N],num[N],id[N];//dx:表示左边的点离源点的距离 bool vis[N];//dfs中用于标记 bool Find(int u) { for(int i = 0; i < g[u].size(); i++) { if(!vis[g[u][i]] && dy[g[u][i]] == dx[u] + 1) { vis[g[u][i]] = true; if(!my[g[u][i]] || Find(my[g[u][i]])) { mx[u] = g[u][i]; my[g[u][i]] = u; return true; } } } return false; } int Hmatch() { memset(mx, 0, sizeof(mx)); memset(my, 0, sizeof(my)); int ans = 0; while(true) { bool flag = false; while(!que.empty()) que.pop(); memset(dx, 0, sizeof(dx)); memset(dy, 0, sizeof(dy)); for(int i = 1; i <= n1; i++) if(!mx[i])//增广路起源于左边的点 que.push(i); while(!que.empty()) { int u = que.front(); que.pop(); for(int i = 0; i < g[u].size(); i++) if(!dy[g[u][i]]) { dy[g[u][i]] = dx[u] + 1; if(my[g[u][i]]) { dx[my[g[u][i]]] = dy[g[u][i]] + 1; que.push(my[g[u][i]]); } else flag = true; } } if(!flag) break;//上面是寻找增广路 memset(vis, 0, sizeof(vis));//走一条增广路 for(int i = 1; i <= n1; i++) if(!mx[i]&&Find(i)) ans++; } return ans; } void init() { n1=0,n2=0; scanf("%d",&n); memset(num, 0, sizeof(num)); memset(id, 0, sizeof(id)); for(int i=1;i<=n;i++) { scanf("%d", &num[i]); g[i].clear(); } sort(num+1, num+n+1); } int main() { int cas = 0; scanf("%d",&t); while(t--) { init(); for(int i = 1; i <= n; i++) { int factor[10000]; int cnt=0,sum=0, k = num[i]; for(int j = 2; k>1 && j*j<=k; j++) if(k%j==0) { factor[cnt++] = j; while(k%j==0) { k/=j; sum++; } } if(k>1) { //k有可能是质数 factor[cnt++]=k; sum++; } if(sum % 2 == 0) id[num[i]] = (++n1); else id[num[i]] = (++n2); for(int j = 0; j < cnt; j++) { int c = num[i]/factor[j]; if(id[c]) { if(sum&1) g[id[c]].push_back(id[num[i]]); else g[id[num[i]]].push_back(id[c]); } } } printf("Case %d: %d\n", ++cas, n - Hmatch()); } return 0; }