凡是考智商的题里面总会有这么一种消除游戏。不过现在面对的这关连连看可不是QQ游戏里那种考眼力的游戏。我们的规则是,给出一个闭区间[a,b]中的全部整数,如果其中某两个数x,y(设x>y)的平方差x2-y2是一个完全平方数z2,并且y与z互质,那么就可以将x和y连起来并且将它们一起消除,同时得到x+y点分数。那么过关的要求就是,消除的数对尽可能多的前提下,得到足够的分数。快动手动笔算一算吧。
凡是考智商的题里面总会有这么一种消除游戏。不过现在面对的这关连连看可不是QQ游戏里那种考眼力的游戏。我们的规则是,给出一个闭区间[a,b]中的全部整数,如果其中某两个数x,y(设x>y)的平方差x2-y2是一个完全平方数z2,并且y与z互质,那么就可以将x和y连起来并且将它们一起消除,同时得到x+y点分数。那么过关的要求就是,消除的数对尽可能多的前提下,得到足够的分数。快动手动笔算一算吧。
只有一行,两个整数,分别表示a,b。
两个数,可以消去的对数,及在此基础上能得到的最大分数。
对于30%的数据,1<=a,b<=100
对于100%的数据,1<=a,b<=1000
费用流的应用
这道题想了很久应该如何限定每个数只能选一次,后来看了题解才发现不是这样子。
对于每一个数i,我们拆成两个点i1和i2。对于每一个符合条件的数对(i,j),加边(i1,j2,1,i+j)(j1,i2,1,i+j),然后从源点向所有i1连边(s,i1,1,0),从所有i2向汇点连边(i2,t,1,0)。跑最大费用最大流。(只要把所有边的费用取反即可,这里负环一定不存在,所以就不用考虑了)
你可能会问:这样每个点不就可能被选两次了吗?但实际上(i1,j2)(j1,i2)这两条边一定同时被选或同时不选。为什么呢?通俗一点理解,因为两条边容量、费用完全一样。
所以最终答案只要除以2就可以了。
那消去的对数如何解决呢?统计一下所有指向汇点t的边中被选的个数,即流量为1的个数。记得最后输出时答案要除以2。
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> #include<algorithm> #include<queue> #define F(i,j,n) for(int i=j;i<=n;i++) #define D(i,j,n) for(int i=j;i>=n;i--) #define ll long long #define pa pair<int,int> #define maxn 2010 #define maxm 2000000 #define inf 1000000000 using namespace std; struct edge_type { int from,to,next,v,c; }e[maxm]; int head[maxn],dis[maxn],p[maxn]; int a,b,s,t,cnt=1,ans=0,tot=0; bool inq[maxn]; inline void add_edge(int x,int y,int v,int c) { e[++cnt]=(edge_type){x,y,head[x],v,c};head[x]=cnt; e[++cnt]=(edge_type){y,x,head[y],0,-c};head[y]=cnt; } inline int spfa() { queue<int>q; memset(inq,false,sizeof(inq)); F(i,1,t) dis[i]=inf; dis[s]=0;q.push(s);inq[s]=true; while (!q.empty()) { int x=q.front();q.pop();inq[x]=false; for(int i=head[x];i;i=e[i].next) { int y=e[i].to; if (e[i].v&&dis[y]>dis[x]+e[i].c) { dis[y]=dis[x]+e[i].c; p[y]=i; if (!inq[y]){q.push(y);inq[y]=true;} } } } return dis[t]!=inf; } inline void mcf() { ans=0; while (spfa()) { int tmp=inf; for(int i=p[t];i;i=p[e[i].from]) tmp=min(tmp,e[i].v); ans+=tmp*dis[t]; for(int i=p[t];i;i=p[e[i].from]){e[i].v-=tmp;e[i^1].v+=tmp;} } } inline bool check(int x,int y) { int tmp=x*x-y*y,z=(int)sqrt(tmp); if (z*z!=tmp) return false; if (y<z) swap(y,z); while (z){tmp=y%z;y=z;z=tmp;} return (y==1); } int main() { scanf("%d%d",&a,&b); s=2001;t=2002; F(i,a,b-1) F(j,i+1,b) if (check(j,i)) { add_edge(i,j+1000,1,-i-j); add_edge(j,i+1000,1,-i-j); } F(i,a,b) { add_edge(s,i,1,0); add_edge(i+1000,t,1,0); } mcf(); for(int i=2;i<=cnt;i+=2) if (e[i].to==t&&!e[i].v) tot++; printf("%d %d\n",tot/2,-ans/2); }