#include <stdio.h> int main() { puts("转载请注明出处谢谢"); puts("http://blog.csdn.net/vmurder/article/details/43272509"); }
题解:
如果i、j不能共存,那么就中间连一条边,然后i连源流量b[i],j连汇流量b[j],就可以满足性质。
但是问题是哪个点连源,哪个点连汇呢?
这种做法需要图是二分图,这样某些点就可以只连源,某些点只连汇。
当且仅当这种情况才可以有上述建图。
而这道题有个很好的性质:
任意两个奇数肯定满足条件一(把奇数看成2k+1,然后最后形式是2*奇数),
任意两个偶数肯定满足条件二(gcd至少是2)。
所以不妨奇连源,偶连汇,(反过来也一样),这样就解决了这个问题。
代码:
#include <queue> #include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define N 1010 #define M 1001000 #define inf 0x3f3f3f3f using namespace std; struct KSD { int v,len,next; }e[M]; int head[N],cnt; inline void add(int u,int v,int len) { e[++cnt].v=v; e[cnt].len=len; e[cnt].next=head[u]; head[u]=cnt; } int s,t,d[N]; queue<int>q; bool bfs() { while(!q.empty())q.pop(); memset(d,0,sizeof d); int i,u,v; q.push(s),d[s]=1; while(!q.empty()) { u=q.front(),q.pop(); for(i=head[u];i;i=e[i].next) { if(!d[v=e[i].v]&&e[i].len) { d[v]=d[u]+1; if(v==t)return 1; q.push(v); } } } return 0; } int dinic(int x,int flow) { if(x==t)return flow; int remain=flow,i,v,k; for(i=head[x];i&&remain;i=e[i].next) { if(d[v=e[i].v]==d[x]+1&&e[i].len) { k=dinic(v,min(e[i].len,remain)); if(!k)d[v]=0; e[i].len-=k,e[i^1].len+=k; remain-=k; } } return flow-remain; } int gcd(int a,int b){return b==0?a:gcd(b,a%b);} bool check(long long x,long long y) { if(gcd(x,y)>1)return 1; long long T=x*x+y*y; long long sqrtT=sqrt(T); if(sqrtT*sqrtT!=T)return 1; return 0; } int n,maxflow,a[N],b[N]; void build() { int i,j,k; scanf("%d",&n),s=n+1,t=n+2,cnt=1; for(i=1;i<=n;i++)scanf("%d",&a[i]); for(i=1;i<=n;i++)scanf("%d",&b[i]),maxflow+=b[i]; for(i=1;i<=n;i++) { if(a[i]&1)add(s,i,b[i]),add(i,s,0); else add(i,t,b[i]),add(t,i,0); } for(i=1;i<=n;i++)for(j=1;j<=n;j++) if((a[i]&1)&&((a[j]&1)==0)&&!check(a[i],a[j])) add(i,j,inf),add(j,i,0); } int main() { freopen("test.in","r",stdin); build(); while(bfs())maxflow-=dinic(s,inf); printf("%d\n",maxflow); return 0; }