BZOJ 3275: Number Minimum Cut

Posted by zypher11 on Wed, 31 Jul 2019 01:03:55 +0200

title

BZOJ 3275
Description

There are N positive integers, from which we need to select some numbers to make the sum of these numbers the largest.
If two numbers a and B satisfy the following conditions at the same time, then a and B cannot be selected at the same time
1: There is a positive integer C, so that a * a+b * b=c * c
2:gcd(a,b)=1

Input

The first line is a positive integer n, representing the number of numbers. N<=3000
The second line contains n positive integers a1,a2,... an

Output

Maximum sum

Sample Input

5
3 4 5 6 7

Sample Output

22

analysis

Now I feel like a very routine topic. Let's talk about drawing directly.

  1. The dismantling point, because it can only be taken once, the source point is connected to the entry point, the exit point is connected to the confluence point, and the capacity is a[i]a[i]a[i];
  2. The two points satisfying the condition of the topic are connected, and the capacity is INFINFINF.
  3. Running maximum flow and minimum cut;
  4. The final answer is a[i] maxflow/2 sum a[i] - maxflow/2 a[i] maxflow/2 (code code code is sum ans / 2sum ans/2 sum ans/2).

code

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10,maxm=1e6+10,inf=0x3f3f3f3f;
 
char buf[1<<15],*fs,*ft;
inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
template<typename T>inline void read(T &x)
{
    x=0;
    T f=1, ch=getchar();
    while (!isdigit(ch) && ch^'-') ch=getchar();
    if (ch=='-') f=-1, ch=getchar();
    while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
    x*=f;
}
 
template<typename T>inline void write(T x)
{
    if (!x) { putchar('0'); return ; }
    if (x<0) putchar('-'), x=-x;
    T num=0, ch[20];
    while (x) ch[++num]=x%10+48, x/=10;
    while (num) putchar(ch[num--]);
}
 
int ver[maxm],edge[maxm],Next[maxm],head[maxn],len=1;
inline void add(int x,int y,int z)
{
    ver[++len]=y,edge[len]=z,Next[len]=head[x],head[x]=len;
    ver[++len]=x,edge[len]=0,Next[len]=head[y],head[y]=len;
}
 
int s,t;
int dist[maxn];
inline bool bfs()
{
    queue<int>q;
    memset(dist,0,sizeof(dist));
    q.push(s);dist[s]=1;
    while (!q.empty())
    {
        int x=q.front();
        q.pop();
        for (int i=head[x]; i; i=Next[i])
        {
            int y=ver[i];
            if (edge[i] && !dist[y])
            {
                dist[y]=dist[x]+1;
                if (y==t) return 1;
                q.push(y);
            }
        }
    }
    return 0;
}
 
inline int get(int x,int low)
{
    if (x==t) return low;
    int tmp=low;
    for (int i=head[x]; i; i=Next[i])
    {
        int y=ver[i];
        if (edge[i] && dist[y]==dist[x]+1)
        {
            int a=get(y,min(tmp,edge[i]));
            if (!a) dist[y]=0;
            edge[i]-=a;
            edge[i^1]+=a;
            if (!(tmp-=a)) break;
        }
    }
    return low-tmp;
}
 
inline int gcd(int a,int b)
{
    return b?gcd(b,a%b):a;
}
 
inline bool check(int a,int b)//Conditions for this question
{
    int s=a*a+b*b,q=(int)sqrt(s);
    if (q*q!=s || gcd(a,b)!=1) return 0;
    return 1;
}
 
int a[maxn];
int main()
{
    int n;read(n);
    int sum=0,ans=0;
    s=0,t=n<<1|1;
    for (int i=1; i<=n; ++i) read(a[i]),add(s,i,a[i]),add(i+n,t,a[i]),sum+=a[i];//Disassemble points, each can only be taken once
    for (int i=1; i<=n; ++i)
        for (int j=i+1; j<=n; ++j)
            if (check(a[i],a[j])) add(i,j+n,inf),add(j,i+n,inf);//If you satisfy the conditions, take one and the other.
    while (bfs()) ans+=get(s,inf);
    write(sum-ans/2);//So the number of final choices is double, so divide by two.
    return 0;
}