BZOJ 3275.Number——二分图、网络流最小割

该博客探讨了一种特殊问题,如何在满足正整数勾股数条件和互质性的限制下,从给定的n个数中选择组合以获得最大的和。作者提出了利用网络流和二部图技巧,以及暴力搜索验证的解决方案,并展示了如何使用Dinic算法求解。
摘要由CSDN通过智能技术生成

题目描述

有N个正整数,需要从中选出一些数,使这些数的和最大。
若两个数a,b同时满足以下条件,则a,b不能同时被选
1:存在正整数C,使a*a+b*b=c*c
2:gcd(a,b)=1
n≤3000

题解

我们可以很容易地在 O ( n 2 log ⁡ a ) O(n^2\log a) O(n2loga) 的时间内处理出哪些数是不能同时被选的。然后按照套路,用网络流解决这个问题。

我们可以先假定所有数都被选了,然后用最小割来求出需要去掉的数的最小和。

但是题目的限制很奇怪,不是“a选了b必须选”的形式,而是“a选了b不能选”,所以我们的图不能简单地定义为“与S相连的点表示被选,与T相连的点表示不选”这种统一形式。

这题让我想到了另一道题,也是“a选了b不能选”的限制。那道题由于限制的连边构成网格图,所以可以对点进行黑白染色解决,即一部分点的定义是“与S相连表示被选,与T相连表示不选”,另一部分是“与S相连表示不选,与T相连表示被选”,这样有边(限制条件)相连的点定义必然不同,就转化为了“a选了b必须选”的限制。

这种技巧的本质其实是把点分为一个二部图,同一半的点之间互相没有连边。这道题是否也可以用这种技巧?

我们发现,这道题的边非常特殊,要求两个数是最简勾股数则不能同时选。而勾股数实际上是非常稀有的,通过找人帮忙暴力打表发现,最简勾股数的关系在很大范围内不会出现环。无环的图是一定可以转化成二部图的。

然后这题就做完了。我很少做网络流的题,所以不太会分析这种玄学复杂度,反正Dinic随便过就是了。

代码

#include<cstdio>//JZM yyds!!
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define ll long long
#define uns unsigned
#define MOD 
#define MAXN 100005
#define INF 1e17
#define IF (it->first)
#define IS (it->second)
using namespace std;
inline ll read(){
	ll x=0;bool f=1;char s=getchar();
	while((s<'0'||s>'9')&&s>0)f^=(s=='-'),s=getchar();
	while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
	return f?x:-x;
}
int pt[30],lp;
inline void print(ll x,char c='\n'){
	if(x<0)putchar('-'),x=-x;
	pt[lp=1]=x%10;
	while(x>9)x/=10,pt[++lp]=x%10;
	while(lp)putchar(pt[lp--]^48);
	putchar(c);
}
inline ll lowbit(ll x){return x&-x;}

struct edge{
	int v,to;ll f;edge(){}
	edge(int V,ll F,int T){v=V,f=F,to=T;}
}e[1000005];
int EN=1,G[MAXN],ps;
inline void addedge(int u,int v,ll f){
	e[++EN]=edge(v,f,G[u]),G[u]=EN;
	e[++EN]=edge(u,0,G[v]),G[v]=EN;
}
int cur[MAXN],ds[MAXN];
queue<int>q;
inline bool dcbfs(int S,int T){
	for(int i=1;i<=ps;i++)ds[i]=-1;
	while(!q.empty())q.pop();
	q.push(S),ds[S]=0;
	while(!q.empty()){
		int u=q.front();q.pop();
		if(u==T)continue;
		for(int i=G[u];i;i=e[i].to){
			int v=e[i].v;
			if(e[i].f>0&&ds[v]<0)ds[v]=ds[u]+1,q.push(v);
		}
	}return ds[T]>=0;
}
inline ll dcdfs(int x,int T,ll lim){
	if(x==T)return lim;
	ll res=lim;
	for(int i=cur[x];i&&res;i=e[i].to){
		cur[x]=i;
		int v=e[i].v;
		if(ds[v]==ds[x]+1&&e[i].f>0){
			ll ad=dcdfs(v,T,min(e[i].f,res));
			e[i].f-=ad,res-=ad,e[i^1].f+=ad;
		}
	}return lim-res;
}
inline ll dinic(int S,int T){
	ll res=0;
	while(dcbfs(S,T)){
		for(int i=1;i<=ps;i++)cur[i]=G[i];
		while(ll ad=dcdfs(S,T,INF))res+=ad;
	}return res;
}

inline ll gcd(ll a,ll b){return !b?a:gcd(b,a%b);}
int n,m,S,T,a[MAXN],cl[MAXN];
bool vis[MAXN];
vector<int>D[MAXN];
inline bool check(ll a,ll b){
	ll c=a*a+b*b,qc=sqrt(c);
	return gcd(a,b)==1&&qc*qc==c;
}
bool ok=1;
inline void dfs(int x,int c){
	cl[x]=c,vis[x]=1;
	for(auto v:D[x]){
		if(!vis[v])dfs(v,c^1);
		else if(cl[v]==cl[x])ok=0;
	}
}
ll sum;
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)a[i]=read(),sum+=a[i];
	for(int i=1;i<n;i++)
		for(int j=i+1;j<=n;j++)
			if(check(a[i],a[j]))
				D[i].push_back(j),D[j].push_back(i);
	for(int i=1;i<=n;i++)if(!vis[i])dfs(i,2);
	ps=n,S=++ps,T=++ps;
	for(int i=1;i<=n;i++){
		if(cl[i]==2){
			addedge(S,i,a[i]);
			for(auto v:D[i])addedge(i,v,INF);
		}else addedge(i,T,a[i]);
	}
	printf("%lld\n",sum-dinic(S,T));
	return 0;
}

upd 1 day later

我并不知道这个无环的结论是否成立,但是至少看来无奇环是成立的,而且是可以证明的。因为奇数与奇数不可能是勾股数,偶数与偶数不可能互质,所以奇偶分类必定是个无奇环的二分图。

然而已经有一个巨佬 O n e I n D a r k \rm O\color{red}neInDark OneInDark 用树形DP过了这题,难道说猜测的无环的结论真的是对的?

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值