您的位置:首页 > 其它

bzoj2661【Beijing WC 2012】连连看

2015-12-14 23:11 399 查看

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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: