bzoj3158&3275: 千钧一发(最小割)

3158: 千钧一发

题目:传送门

题解:

   这是一道很好的题啊...极力推荐

   细看题目:要求一个最大价值,那么我们可以转换成求损失的价值最小

   那很明显就是最小割的经典题目啊?!

   但是这里两个子集的分化并不明显...GG

   耐心一点,从题目的要求再入手:

   对于第二个要求,如果两点的a值都为偶数,那么肯定满足

   那如果两个数都为奇数的话,也必定满足要求一,证明如下:

   1、一个奇数的平方%4为1,一个偶数的平方%4为0

   2、两个奇数的平方和%4为2

   3、如果两个奇数的平方和是一个奇数的平方,那么%4应该为1,不符合

   4、如果两个奇数的平方和是一个偶数的平方,那么%4应该为0,不符合

   因此得证。

   这样子思考的话,两个子集的分化就较为明显了:

   st向a值为奇数的相连,a值为偶数的向ed相连,容量都为b值;这样子所形成的两个子集里面的点一定都是符合要求的。

   最后一步,也是最关键的一步:

   两个子集之间两两匹配,如果当前匹配的两个点是不符合要求的,就将这两个点相连,容量为无限大。

   有什么用呢?自己画几个图便很容易理解:

   这时候我们跑最小割的话,割出来的边就是损失价值的最小值

   用sum-最小割就是答案啊

代码:

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<cstdlib>
  4 #include<cmath>
  5 #include<algorithm>
  6 #define qread(x) x=read()
  7 using namespace std;
  8 typedef long long LL;
  9 const LL inf=999999999;
 10 LL n,st,ed,sum;
 11 LL A[1100],B[1100];
 12 inline LL read()
 13 {
 14     LL f=1,x=0;char ch;
 15     while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
 16     while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
 17     return f*x;
 18 }
 19 struct node
 20 {
 21     LL x,y,c,next,other;
 22 }a[2100000];LL len,last[110000];
 23 void ins(LL x,LL y,LL c)
 24 {
 25     int k1,k2;
 26     k1=++len;
 27     a[len].x=x;a[len].y=y;a[len].c=c;
 28     a[len].next=last[x];last[x]=len;
 29     
 30     k2=++len;
 31     a[len].x=y;a[len].y=x;a[len].c=0;
 32     a[len].next=last[y];last[y]=len;
 33     
 34     a[k1].other=k2;
 35     a[k2].other=k1;
 36 }
 37 LL list[11000],h[11000],head,tail;
 38 bool bt_h()
 39 {
 40     memset(h,0,sizeof(h));h[st]=1;
 41     list[1]=st;head=1;tail=2;
 42     while(head!=tail)
 43     {
 44         int x=list[head];
 45         for(int k=last[x];k;k=a[k].next)
 46         {
 47             int y=a[k].y;
 48             if(h[y]==0 && a[k].c)
 49             {
 50                 h[y]=h[x]+1;
 51                 list[tail++]=y;
 52             }
 53         }
 54         head++;
 55     }
 56     if(h[ed])return true;
 57     return false;
 58 }
 59 LL find_flow(LL x,LL flow)
 60 {
 61     if(x==ed)return flow;
 62     LL s=0,t;
 63     for(int k=last[x];k;k=a[k].next)
 64     {
 65         int y=a[k].y;
 66         if(h[y]==h[x]+1 && a[k].c>0 && flow>s)
 67         {
 68             s+=t=find_flow(y,min(a[k].c,flow-s));
 69             a[k].c-=t;a[a[k].other].c+=t;
 70         }
 71     }
 72     if(s==0)h[x]=0;
 73     return s;
 74 }
 75 LL gcd(LL a,LL b)
 76 {
 77     return a==0?b:gcd(b%a,a);
 78 }
 79 bool pd(LL x,LL y)
 80 {
 81     LL T=x*x+y*y,t=sqrt(T);
 82     if(t*t!=T)return true;
 83     if(gcd(x,y)>1)return true;
 84     return false;
 85 }
 86 int main()
 87 {
 88     sum=0;
 89     qread(n);
 90     len=0;memset(last,0,sizeof(last));
 91     for(int i=1;i<=n;i++)qread(A[i]);
 92     for(int i=1;i<=n;i++){qread(B[i]);sum+=B[i];}
 93     st=n+1;ed=st+1;
 94     for(int i=1;i<=n;i++)
 95     {
 96         if(A[i]%2==1)ins(st,i,B[i]);
 97         else ins(i,ed,B[i]);
 98     }
 99     for(int i=1;i<=n;i++)
100         for(int j=1;j<=n;j++)
101             if((A[i]%2==1) && (A[j]%2==0) && !pd(A[i],A[j]))
102                 ins(i,j,inf);
103     LL ans=0;
104     while(bt_h())ans+=find_flow(st,inf);
105     printf("%lld\n",sum-ans);
106     return 0;
107 }

 

posted @ 2018-02-07 21:52  CHerish_OI  阅读(351)  评论(0编辑  收藏  举报