某二分图匹配题的题解

P1027 叶片

传送门

题意简化

给出 n 个点的转盘,有一些点缺失,你需要再去掉一些点使得转盘重心稳定

那么其实就是要取出一些点和缺失的点构成一个稳定的转盘,这样的话所有取出的点肯定可以表示成多个正多边形

于是这道题可以简化为给定 (n) 个点,然后你用边数为 (n) 的约数的正多边形去框住所有的标记点,每个点最多被框一次,求框住的最少未标记点,那么未标记点最少其实就是总共框住的点数最少

(以下所说的多边形默认为正多边形,并且会出现 2 边形,默认它存在,退化为线)

最优选择

题目中数据范围说 (n) 最多有两个质因子

那么我们先考虑只有一个质因子怎么办

假设 (n=8) 那么我们可以用边数为 2 4 8 的多边形去框点,但其实只用边数为 2 的多边形去框就好了

因为 4 8 都有因子 2 ,可以用 2 表示出来

所以我们只要每次找一个标记点,如果未访问就以当前点为端点开始框多边形就好了

然后我们考虑两个质因子的情况

假设 (n = 12)

那么 6 边形会比 2、3 边形优么?

显然不会,证毕

处理方法

所以我们的问题就是怎么用这两个正多边形去框住所有的标记点

标算是二分图求最大独立集,但其实并不用那么麻烦

我们令 (p1 ,p2) 为 n 的两个质因子

那么我们可以发现把 n 个点分成 (n/(p1·p2)) 份后(也就是隔 (p1·p2) 个点选一个点,总共选 (n/(p1·p2)) 次),这几份点无论如何也不会互相影响,然后我们对于这几份点分别处理就好了

然后我们考虑处理的方式就是对于一份点算出用 (p1) 边形 框的和用 (p2) 边形 框所会框住的点数,然后贪心取小的就行了

正确性

我们发现在长度为 (p1· p2) 的一份点以内一旦选择了一种正多边形去框点,另一种正多边形就会受到影响(即无法放置任何一个另一种正多边形)

那样我们就可以分别计算两个正多边形需要框的最少点数了

处理一种正多边形框点的答案

考虑从未访问的标记点暴力去跑,每次跳正多边形边数个点,然后沿路标记已访问,并累加答案

这样做的复杂度是 (n/p * p =n) 的,所以复杂度就有了保证

代码实现

//by Judge
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#define fp(i,a,b) for(int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(int i=(a),I=(b)-1;i>I;--i)
using namespace std;
const int M=2e4+3;
#ifndef Judge
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
#endif
char buf[1<<21],*p1=buf,*p2=buf;
inline int Min(int a,int b){return a<b?a:b;}
inline int read(){ int x=0,f=1; char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
	for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f;
} int n,m,tmp,ans,num,p[5],vis[M],a[M]; vector<int> vec[M];
inline void solv1(){ int now;  //可以判一个质因子的情况
	fp(i,1,m) if(!vis[a[i]])
		fp(j,1,p[1])
			now=a[i]+(n/p[1])*j,
			now=now%n?now%n:n,
			vis[now]|=1,++ans;
	if(ans==n) puts("-1");
	else printf("%d
",ans-m);
}
inline void solv2(){  //两个质因子的情况
	int p1=p[1],p2=p[2],now=n/p1/p2;
	fp(i,1,m) vec[a[i]%now].push_back(a[i]);
	fp(j,0,now-1){ int res1=0,res2=0;
		fp(i,0,vec[j].size()-1)
			if(!vis[vec[j][i]]) fp(k,1,p1){
				now=vec[j][i]+(n/p1)*k,
				now=now%n?now%n:n,
				vis[now]|=1,++res1;
			}
		fp(i,0,vec[j].size()-1) vis[vec[j][i]]=0;
		
		fp(i,0,vec[j].size()-1)
			if(!vis[vec[j][i]]) fp(k,1,p2){
				now=vec[j][i]+(n/p2)*k,
				now=now%n?now%n:n,
				vis[now]|=1,++res2;
			}
		ans+=Min(res1,res2);  //贪心取最小
	}
	if(ans==n) puts("-1");
	else printf("%d
",ans-m);
}
int main(){ tmp=n=read(),m=read();
	fp(i,1,m) a[i]=read();
	for(int i=2;i*i<=tmp;++i)if(!(tmp%i))
		for(p[++num]=i;!(tmp%i);tmp/=i);
	if(tmp>1) p[++num]=tmp;
	if(!p[2]) return solv1(),0;
	else return solv2(),0;
}
原文地址:https://www.cnblogs.com/Judge/p/10578631.html