题目描述
凡是考智商的题里面总会有这么一种消除游戏。不过现在面对的这关连连看可不是QQ游戏里那种考眼力的游戏。我们的规则是,给出一个闭区间[a,b]中的全部整数,如果其中某两个数x,y(设x>y)的平方差x2-y2是一个完全平方数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


题意十分简单。
这道题我们要保证两个东西:

  • 每个数字只会用一次。
  • 两个数字一起消除。

所以我们建图只要保证上面的条件即可,然后跑费用流。

怎么保证呢?还是同样的套路,拆点,我们把每个数字拆成入点和出点,对于每个数字,我们源点S连向入点,出点连向汇点T,且流量都为1;对于满足要求的一组数字,我们让x的入点连向y的出点,y的入点,连向x的出点。

为什么呢?主要是保证每个数字用一次,因为我们把数字拆开了,那么可能同一个数字的入点和出点不是匹配的同一个数字,这样我们能保证匹配到同一个数字,但是我们计算是×2了,最后除以2即可。


AC代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=1e5+10;
int l,r,s,t,v[N],e[N],d[N];
int head[N<<4],nex[N<<4],to[N<<4],w[N<<4],flow[N<<4],tot=1;
inline void ade(int a,int b,int c,int d){
	to[++tot]=b; w[tot]=d; flow[tot]=c; nex[tot]=head[a]; head[a]=tot;
}
inline void add(int a,int b,int c,int d){
	ade(a,b,c,d);	ade(b,a,0,-d);
}
int spfa(){
	memset(d,0xcf,sizeof d);	d[s]=0;	queue<int> q;	q.push(s);
	int vis[N]={0};	vis[s]=1;
	while(q.size()){
		int u=q.front();	q.pop();	vis[u]=0;
		for(int i=head[u];i;i=nex[i]){
			if(flow[i]&&d[to[i]]<d[u]+w[i]){
				d[to[i]]=d[u]+w[i];
				v[to[i]]=u;	e[to[i]]=i;
				if(!vis[to[i]])	q.push(to[i]),vis[to[i]]=1;
			}
		}
	}
	return d[t]!=0xcfcfcfcf;
}
void EK(){
	int res=0,maxflow=0;
	while(spfa()){
		int mi=0x3f3f3f3f;
		for(int i=t;i!=s;i=v[i])	mi=min(mi,flow[e[i]]);
		for(int i=t;i!=s;i=v[i])	flow[e[i]]-=mi,flow[e[i]^1]+=mi;
		maxflow+=mi;	res+=mi*d[t];
	}
	cout<<maxflow/2<<' '<<res/2<<endl;
}
signed main(){
	cin>>l>>r;	s=0; t=r*2+1;
	for(int i=l;i<=r;i++){
		for(int j=i+1;j<=r;j++){
			if(__gcd(i,j)!=1)	continue;
			int k=j*j-i*i;	if((int)sqrt(k)*(int)sqrt(k)!=k)	continue;
			add(i,j+r,1,i+j);	add(j,i+r,1,i+j);
		}
	}
	for(int i=l;i<=r;i++)	add(s,i,1,0),add(i+r,t,1,0);
	EK();
	return 0;
}