[ASDFZ]电风扇的重心

Description

把一个\(n\)片扇叶的电风扇掉了\(k\)片叶子,问至少还要拆掉几片叶子才能使电风扇的重心回到转轴上,求一种满足条件的方案.

HINT

\(n<2\times10^4,n\)有至多2个质因子.

Solution

一个状态的重心在转轴上,当且仅当可以把其拆成若干个相邻扇叶的距离相等的状态.
对于任意一个相邻扇叶的距离相等为\(k\)的状态,可以把其拆成\(x\)个相邻扇叶的距离相等为\(\frac{k}{x}\)的状态\((k|x)\).
所以对\(n\)分解质因数.
如果\(n=1\),不可行.
如果\(n\)只有\(1\)个质因子\(p\),枚举起点,以\(\frac{n}{p}-1\)为间隔,判断每一圈是否完整:不完整,拆掉这圈没掉的;完整,保留.
如果\(n\)只有\(2\)个质因子\(p_1,p_2\),考虑二分图.
判断以\(\frac{n}{p_1}-1\)的每一圈\(i\)是否完整,完整的话\((s,i)=p_1\).
判断以\(\frac{n}{p_2}-1\)的每一圈\(i\)是否完整,完整的话\((i,t)=p_2\).
对有交点于圈的点集\(x,y:(x,y)=+\infty\).
求最小割,没被割去的即为保留的.
判断割去:对于每一条初始容量为\(+\infty\)的边,如果两端点向源/汇的边只有\(1\)条满流,则满流边被割去;如果两端点向源/汇的边都满流,则初始容量大的满流边被割去.

#include<cmath>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 20005
#define M 20000000
#define INF 20005
#define min(a,b) (a<b?a:b)
#define max(a,b) (a>b?a:b)
using namespace std;
struct graph{
	int nxt,to,f;
}e[M];
int a[N],f[N],tot[N],g[N<<1],dep[N<<1],p[3],n,k,s,t,cnt;
bool b[N],v[N];queue<int> q;
inline int read(){
	int ret=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)){
		ret=(ret<<1)+(ret<<3)+c-'0';c=getchar();
	}
	return ret;
}
inline void addedge(int x,int y,int f){
	e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y;e[cnt].f=f;
}
inline void adde(int x,int y,int f){
	addedge(x,y,f);addedge(y,x,0);
}
inline bool bfs(int u){
	memset(dep,0,sizeof(dep));
	q.push(u);dep[u]=true;
	while(!q.empty()){
		u=q.front();q.pop();
		for(int i=g[u];i;i=e[i].nxt)
			if(e[i].f>0&&!dep[e[i].to]){
				q.push(e[i].to);
				dep[e[i].to]=dep[u]+1;
			}
	}
	return dep[t];
}
inline int dfs(int u,int f){
	if(u==t) return f;
	int ret=0;
	for(int i=g[u],d;i&&f;i=e[i].nxt)
		if(e[i].f>0&&dep[e[i].to]>dep[u]){
			d=dfs(e[i].to,min(e[i].f,f));
			e[i].f-=d;e[i^1].f+=d;ret+=d;f-=d;
		}
	if(!ret) dep[u]=-1;
	return ret;
}
inline int dinic(){
	int ret=0;
	while(bfs(s)) ret+=dfs(s,INF);
	return ret;
}
inline bool chk(int i,int l){
	for(int j=i;j<=n;j+=l)
		if(b[j]) return false;
	return true;
}
inline void Aireen(){
	n=read();k=read();
	if(n==1){
		puts("-1");return; 
	}
	for(int i=1,j;i<=k;++i){
		j=read();b[j]=true;
	}
	for(int i=2,l=n;i<=n&&l>1;++i){
		if(!(l%i)){
			while(!(l%i)) l/=i;
			p[++cnt]=i;
		}
	}
	if(p[2]){
		cnt=1;s=(n<<1)+1;t=s+1;
		for(int i=1,l=n/p[1];i<=l;++i)
			if(chk(i,l)) adde(s,i,p[1]);
		for(int i=1,l=n/p[2];i<=l;++i)
			if(chk(i,l)) adde(i+n,t,p[2]);
		for(int i=1,l=n/p[1];i<=l;++i)
			if(chk(i,l)) for(int j=i;j<=n;j+=l) f[j]=i;
		for(int i=1,l=n/p[2];i<=l;++i){
			for(int j=i;j<=n;j+=l){
				if(!tot[f[j]]&&f[j]&&chk(i,l)) adde(f[j],i+n,INF);
				++tot[f[j]];
			}
			for(int j=i;j<=n;j+=l) --tot[f[j]];
		}
		dinic();
		for(int i=g[s];i;i=e[i].nxt)
			if(!(i&1)&&e[i].f){
				for(int j=e[i].to,l=n/p[1];j<=n;j+=l)
					b[j]=true;
			}
			else v[e[i].to]=true;
		for(int i=g[t];i;i=e[i].nxt)
			if((i&1)&&e[i^1].f){
				for(int j=e[i].to-n,l=n/p[2];j<=n;j+=l)
					b[j]=true;
			}
			else if((i&1)&&!e[i^1].f){
				for(int j=g[e[i].to];j;j=e[j].nxt)
					if(v[e[j].to]){
						for(int o=e[j].to,l=n/p[1];o<=n;o+=l)
							b[o]=true;
					}
			}
		cnt=0;
		for(int i=1;i<=n;++i)
			if(!b[i]) a[++cnt]=i;
	}
	else{
		cnt=0;
		for(int i=1,l=n/p[1];i<=l;++i)
			if(!chk(i,l))
				for(int j=i;j<=n;j+=l)
					if(!b[j]) a[++cnt]=j,b[j]=true;
	}
	if(cnt==n-k){
		printf("-1");return;
	}
	printf("%d\n",cnt);
	for(int i=1;i<=cnt;++i)
		printf("%d ",a[i]);
	printf("\n");
}
int main(){
	freopen("c.in","r",stdin);
	freopen("c.out","w",stdout);
	Aireen();
	fclose(stdin);
	fclose(stdout);
	return 0;
}

2017-03-29 00:04:25

posted @ 2021-11-26 20:10  Aireen_Ye  阅读(54)  评论(0编辑  收藏  举报
底部 顶部 留言板 归档 标签
Der Erfolg kommt nicht zu dir, du musst auf den Erfolg zugehen.