bzoj 3158: 千钧一发 最小割

题意

bzoj 3158: 千钧一发 最小割_第1张图片
n<=1000,1<=ai,bi<=10^6

分析

若两个点不能同时选,则在它们之间连一条边。可以保证这样连出来的图一定是一个二分图。
证明:显然所有偶数对满足条件2,所有奇数对满足条件1。
因为(2a+1)^2+(2b+1)^2=2(2a^2+2a+2b^2+2b+1)
直接上最小割即可。

代码

#include
#include
#include
#include
#include
#include
#include
using namespace std;

typedef long long LL;

const int N=1005;
const int inf=1000000000;

int n,a[N],b[N],s,t,cnt,last[N],cur[N],dis[N];
queue<int> que;
struct edge{
    int to,c,next;}e[N*N*2];

int read()
{
    int x=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9'){
    if(ch=='-')f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9'){
    x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

int gcd(int x,int y)
{
    if (!y) return x;
    else return gcd(y,x%y);
}

LL sqr(LL x) {
    return x*x;}

bool check(int x,int y)
{
    if (gcd(x,y)>1) return 1;
    if (sqr((LL)sqrt(sqr(x)+sqr(y)))!=sqr(x)+sqr(y)) return 1;
    return 0;
}

void addedge(int u,int v,int c)
{
    e[++cnt].to=v;e[cnt].c=c;e[cnt].next=last[u];last[u]=cnt;
    e[++cnt].to=u;e[cnt].c=0;e[cnt].next=last[v];last[v]=cnt;
}

bool bfs()
{
    for (int i=s;i<=t;i++) dis[i]=0;
    while (!que.empty()) que.pop();
    dis[s]=1;que.push(s);
    while (!que.empty())
    {
        int u=que.front();que.pop();
        for (int i=last[u];i;i=e[i].next)
            if (e[i].c&&!dis[e[i].to])
            {
                dis[e[i].to]=dis[u]+1;
                if (e[i].to==t) return 1;
                que.push(e[i].to);
            }
    }
    return 0;
}

int dfs(int x,int maxf)
{
    if (x==t||!maxf) return maxf;
    int ret=0;
    for (int &i=cur[x];i;i=e[i].next)
        if (e[i].c&&dis[e[i].to]==dis[x]+1)
        {
            int f=dfs(e[i].to,min(e[i].c,maxf-ret));
            e[i].c-=f;
            e[i^1].c+=f;
            ret+=f;
            if (maxf==ret) break;
        }
    return ret;
}

int dinic()
{
    int ans=0;
    while (bfs())
    {
        for (int i=s;i<=t;i++) cur[i]=last[i];
        ans+=dfs(s,inf);
    }
    return ans;
}

int main()
{
    n=read();int ans=0;
    for (int i=1;i<=n;i++) a[i]=read();
    for (int i=1;i<=n;i++) b[i]=read(),ans+=b[i];
    s=0;t=n+1;cnt=1;
    for (int i=1;i<=n;i++)
    {
        if (a[i]%2==0)
        {
            addedge(s,i,b[i]);
            for (int j=1;j<=n;j++)
                if (a[j]%2==1&&!check(a[i],a[j])) addedge(i,j,inf);
        }
        else addedge(i,t,b[i]);
    }
    ans-=dinic();
    printf("%d",ans);
    return 0;
}

你可能感兴趣的:(网络流)