题目描述
凡是考智商的题里面总会有这么一种消除游戏。不过现在面对的这关连连看可不是QQ游戏里那种考眼力的游戏。我们的规则是,给出一个闭区间[a,b]中的全部整数,如果其中某两个数x,y(设x>y)的平方差x^2-y^2是一个完全平方数z^2,并且y与z互质,那么就可以将x和y连起来并且将它们一起消除,同时得到x+y点分数。那么过关的要求就是,消除的数对尽可能多的前提下,得到足够的分数。快动手动笔算一算吧。
输入格式
只有一行,两个整数,分别表示a,b。
输出格式
两个数,可以消去的对数,及在此基础上能得到的最大分数。
输入输出样例
输入 #1复制
1 15
输出 #1复制
2 34
说明/提示
对于30%的数据,1<=a,b<=100
对于100%的数据,1<=a,b<=1000
思路
从题目中可看出这题为拆点并跑最大流最大费用。对于每个符合题目条件的(i,j),我们拆为i,i+b,j,j+b。并将i与j+b,i+b与j相连,建边,这样虽然可以省去自己与自己建边,但需要将结果除去2。
代码
#include<bits/stdc++.h> #define N 10700 #define M 107000 #define inf 1<<29 using namespace std; struct node{ int y,z,p,next; }e[M*2]; int tot=1,head[N],maxflow=0,ans=0,s,t; void add(int x,int y,int z,int p){ e[++tot].y=y;e[tot].z=z;e[tot].p=p; e[tot].next=head[x];head[x]=tot; } int incf[N],v[N],pre[N],d[N]; bool spfa(){ queue<int> q; memset(d,0xcf,sizeof(d)); memset(v,0,sizeof(v)); q.push(s);d[s]=0;v[s]=1; incf[s]=inf; while(q.size()){ int x=q.front();v[x]=0;q.pop(); for(int i=head[x];i;i=e[i].next){ int y=e[i].y,z=e[i].z; if(!z) continue; if(d[y]<d[x]+e[i].p){ d[y]=d[x]+e[i].p; incf[y]=min(incf[x],z); pre[y]=i; if(!v[y]) v[y]=1,q.push(y); } } } if(d[t]==0xcfcfcfcf) return false; return true; } void update(){ int x=t; while(x!=s){ int i=pre[x]; e[i].z-=incf[t]; e[i^1].z+=incf[t]; x=e[i^1].y; } maxflow+=incf[t]; ans+=d[t]*incf[t]; } int n=1000; int gcd(int a,int b){ return b==0?a:gcd(b,a%b); } int f(int x,int y){ int k=x*x-y*y; int g=sqrt(k); if(g*g==k&&gcd(y,g)==1) return 1; else return 0; } int main() { int a,b; cin>>a>>b; s=0;t=b+1;n=b; for(int i=a;i<=b;i++) add(s,i,1,0),add(i,s,0,0); for(int i=a+b;i<=b+b;i++) add(i,t,1,0),add(t,i,0,0); for(int i=a;i<=b;i++){ for(int j=a;j<i;j++){ if(f(i,j)){ add(j,i+b,1,i+j);add(i+b,j,0,-i-j); add(i,j+b,1,i+j),add(j+b,i,0,-i-j); } } } while(spfa()) update(); cout<<maxflow/2<<" "<<ans/2<<endl; return 0; }