【bzoj 3275】Number(最小割)
Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 718 Solved: 305
[Submit][Status][Discuss]
Description
有N个正整数,需要从中选出一些数,使这些数的和最大。
若两个数a,b同时满足以下条件,则a,b不能同时被选
1:存在正整数C,使a*a+b*b=c*c
2:gcd(a,b)=1
Input
第一行一个正整数n,表示数的个数。
第二行n个正整数a1,a2,?an。
Output
最大的和。
Sample Input
5
3 4 5 6 7
Sample Output
22
HINT
n<=3000。
Source
网络流
【题解 :网络流最小割。】
[原本的思路是将每个点与源点连边、与汇点连边,流量分别为当前数,然后预处理出所有不能共存的数对,并在它们之间连一条流量为INF的边,赶脚十分的科学。然而发现会MLE。。。表示不开森]
【**后来想到刚做完的圈地,黑白染色(黑:奇数,白:偶数),源点与黑点连流量为当前数的边,白点与汇点连流量为当前数的边,把不能共存的数对连一条流量为INF的边。 然后跑最大流(=最小割)】
【从网上得知:可以证明奇数和奇数(不能构成勾股数)、偶数和偶数(最大公因数一定不会是1)是一定可以共存的,所以有可能连边的只可能是奇数和偶数之间所以只要枚举判断每两个奇数和偶数之间能否连边即可(奇数向偶数连边啊,我刚开始忘判断哪个是奇数哪个是偶数,WA成一坨啊 233)**】
【代码】
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF 0x7fffffff
using namespace std;
int a[1200010],next[1200010],p[3010],remain[1200010],tot;
int dis[3010],cur[3010];
int ls[3010],rs[3010],ll,rr;
int c[3010],n,S,T,sum,s,mincut;
int gcd(int a,int b)
{
if (!(a%b)) return b;
else return gcd(b,a%b);
}
inline void add(int x,int y,int flow)
{
tot++; a[tot]=y; next[tot]=p[x]; p[x]=tot; remain[tot]=flow;
tot++; a[tot]=x; next[tot]=p[y]; p[y]=tot; remain[tot]=0;
}
inline bool bfs(int s,int t)
{
queue<int>que;
memset(dis,-1,sizeof(dis));
for(int i=s;i<=t;++i) cur[i]=p[i];
dis[s]=0; que.push(s);
while(!que.empty())
{
int u,v;
u=que.front(); que.pop();
v=p[u];
while(v!=-1)
{
if (remain[v]&&dis[a[v]]<0)
{
dis[a[v]]=dis[u]+1;
que.push(a[v]);
}
v=next[v];
}
}
if(dis[t]<0) return false;
else return true;
}
inline int dfs(int now,int t,int flow)
{
if (!flow||now==t) return flow;
int u=cur[now],s1=0,s;
while(u!=-1)
{
cur[now]=u;
if (dis[a[u]]==dis[now]+1&&(s=dfs(a[u],t,min(flow,remain[u]))))
{
s1+=s; flow-=s;
remain[u]-=s; remain[u^1]+=s;
if (!flow) break;
}
u=next[u];
}
return s1;
}
int main()
{
int i,j;
memset(next,-1,sizeof(next));
memset(p,-1,sizeof(p));
scanf("%d",&n);
T=n+1; tot=-1;
for (i=1;i<=n;++i)
{
scanf("%d",&c[i]);
if (c[i]&1)
add(S,i,c[i]),ls[++ll]=i;
else add(i,T,c[i]),rs[++rr]=i;
sum+=c[i];
}
for (i=1;i<=ll;++i)
for (j=1;j<=rr;++j)
{
long long k=c[ls[i]]*c[ls[i]]+c[rs[j]]*c[rs[j]];
long long t=sqrt(k);
if (t*t==k&&gcd(c[ls[i]],c[rs[j]])==1)
add(ls[i],rs[j],INF);
}
while(bfs(S,T))
while(s=dfs(S,T,0x7fffffff))
mincut+=s;
sum-=mincut;
printf("%d\n",sum);
return 0;
}
既然无能更改,又何必枉自寻烦忧