bzoj2661【Beijing WC 2012】连连看

2661: [BeiJing wc2012]连连看

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 830   Solved: 309
[ Submit][ Status][ Discuss]

Description

 凡是考智商的题里面总会有这么一种消除游戏。不过现在面对的这关连连看可不是QQ游戏里那种考眼力的游戏。我们的规则是,给出一个闭区间[a,b]中的全部整数,如果其中某两个数x,y(设x>y)的平方差x2-y2是一个完全平方数z2,并且y与z互质,那么就可以将x和y连起来并且将它们一起消除,同时得到x+y点分数。那么过关的要求就是,消除的数对尽可能多的前提下,得到足够的分数。快动手动笔算一算吧。

Input

        
 只有一行,两个整数,分别表示a,b。

Output

 两个数,可以消去的对数,及在此基础上能得到的最大分数。

Sample Input

1 15

Sample Output

2 34

HINT

对于30%的数据,1<=a,b<=100

对于100%的数据,1<=a,b<=1000

Source




费用流的应用

这道题想了很久应该如何限定每个数只能选一次,后来看了题解才发现不是这样子。

对于每一个数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);
}


你可能感兴趣的:(网络流,bzoj)