P2765 魔术球问题 最大流||二分图

原题:https://www.luogu.org/problemnew/show/P2765

题解:给了n根柱子,相当于最小路径覆盖是n,考虑用dinic,枚举i,将i拆成i和i+MAXN,S连i,i+MAXN连T。若i与j之间是完全平方关系,连接j与i+MAXN。跑一遍dinic,若不连通,相当于要在加一根柱子,若连通相当于能放在之前的柱子上。枚举到恰好用n根柱子时就行了。暴力输出就行了。

#include<bits/stdc++.h>
#define inf (1<<31)-1
using namespace std;
const int N=11000;
const int M=110000;
const int MAXN=2000;//maxn稍微大一点 
struct E{int to,w,nxt;}data[M<<1];
int n,len,m,ans,S,T,to[N],h[N],tag[N],dep[N],cur[N];
int rt[N],cnt;
queue<int> q;
inline int rd(){
	int x=0;int f=1;char s=getchar();
	while(!isdigit(s)) f=(s=='-'?-1:f),s=getchar();
	while(isdigit(s)) x=(x<<1)+(x<<3)+s-'0',s=getchar();
	return x*f;
}
inline void ins(int x,int y,int w){
	data[++len].to=y;data[len].w=w;data[len].nxt=h[x];h[x]=len;
	data[++len].to=x;data[len].w=0;data[len].nxt=h[y];h[y]=len;
}
bool bfs(){
	while(!q.empty()) q.pop();
	memset(dep,0,sizeof dep);
	dep[S]=1;q.push(S); 
	while(!q.empty()){
		int x=q.front();q.pop();
		for(int i=h[x];i;i=data[i].nxt){
			int y=data[i].to;int w=data[i].w;
			if(!dep[y] && w!=0){
				dep[y]=dep[x]+1;
				q.push(y);
			}
		}
	}
	return dep[T];
} 
int dfs(int x,int lim){
	if(x==T) return lim;
	for(int &i=cur[x];i;i=data[i].nxt){
		int y=data[i].to;int w=data[i].w;
		if(dep[y]==dep[x]+1 && w!=0){
			int di=dfs(y,min(lim,w));
			if(di){
				to[x]=y;
				if(x!=S) tag[max(y-MAXN,0)]=1;
				data[i].w-=di;data[i^1].w+=di;
				return di;
			}	
		}	
	}
	return 0;
} 
int dinic(){
	int ans=0;
	while(bfs()){
		for(int i=0;i<=T;i++) cur[i]=h[i];
		while(int k=dfs(S,inf)) ans+=k; 
	}
	return ans;
}
int main(){
	n=rd();
	S=0;T=5000;m=0;len=1;cnt=0;
	while(cnt<=n){
		++m;int x=m;int y=m+MAXN;
		ins(S,x,1);ins(y,T,1);
		for(int i=1;i<m;i++) if((int) sqrt(i+m)==sqrt(i+m)) ins(i,y,1);
		ans=dinic();
		if(ans==0) rt[++cnt]=m;
	}
	m--;//注意多做了一次 
	printf("%d\n",m); 
	for(int i=1;i<=n;i++){
		printf("%d ",rt[i]);
		int now=rt[i];
		while(to[now] && to[now]!=T) printf("%d ",to[now]-MAXN),now=to[now]-MAXN; 
		printf("\n");
	}
	return 0;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值