bzoj 3158 千钧一发——网络流
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3158
发现偶数之间一定满足第二个条件;奇数之间一定满足第一个条件 ( \( (2m+1)^{2}+(2n+1)^{2}=4m^{2}+4m+1+4n^{2}+4n+1 \),这是个偶数,所以 T2 的 T 一定是偶数;偶数的平方一定是4的倍数,不能有那个 +2 )。
所以如果把不合法的连起来,就是一个二分图。可以用最小割做,不合法之间的连边是 INF 这样。
注意判断第一个条件的时候不用在 12~1414222 之间二分(虽然时间和 gcd 时间一样),只需要求一下根号看看是不是整数就行了。
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define ll long long using namespace std; const int N=1005,M=502005,K=1e6+5,Lm=141425,INF=1e9+5; int n,a[N],b[N],hd[N],xnt=1,cur[N],nxt[M],to[M],cap[M]; int dfn[N],q[N],he,tl; ll s[N]; int Mx(int a,int b){return a>b?a:b;} int Mn(int a,int b){return a<b?a:b;} int gcd(int a,int b){return b?gcd(b,a%b):a;} ll Sqr(int x){return (ll)x*x;} void add(int x,int y,int z) { to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;cap[xnt]=z; to[++xnt]=x;nxt[xnt]=hd[y];hd[y]=xnt;cap[xnt]=0; } bool chk(ll x) { ll k=sqrt(x); return k*k!=x;//!= } bool bfs() { memset(dfn,0,sizeof dfn);dfn[0]=1; q[he=tl=1]=0; while(he<=tl) { int k=q[he++]; for(int i=hd[k],v;i;i=nxt[i]) if(cap[i]&&!dfn[v=to[i]]) dfn[v]=dfn[k]+1,q[++tl]=v; } return dfn[n+1]; } int dinic(int cr,int flow) { if(cr==n+1)return flow; int use=0; for(int& i=cur[cr],v;i;i=nxt[i]) if(cap[i]&&dfn[v=to[i]]==dfn[cr]+1) { int tmp=dinic(v,Mn(flow-use,cap[i])); if(!tmp)dfn[v]=0; use+=tmp;cap[i]-=tmp;cap[i^1]+=tmp; if(use==flow)return use; } return use; } int main() { scanf("%d",&n);int ans=0; for(int i=1;i<=n;i++)scanf("%d",&a[i]),s[i]=Sqr(a[i]); for(int i=1;i<=n;i++)scanf("%d",&b[i]),ans+=b[i]; for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) { if(gcd(a[i],a[j])>1||chk(s[i]+s[j]))continue; a[i]&1?add(i,j,INF):add(j,i,INF); } for(int i=1;i<=n;i++) if(a[i]&1)add(0,i,b[i]); else add(i,n+1,b[i]); while(bfs())memcpy(cur,hd,sizeof hd),ans-=dinic(0,INF); printf("%d\n",ans); return 0; }