BZOJ 2661 [BeiJing wc2012]连连看 费用流
[BeiJing wc2012]连连看
题目连接:
http://www.lydsy.com/JudgeOnline/problem.php?id=2661
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
1<=a,b<=1000
题意
题解:
费用流的水题,如果i,j之间能够删去的话,就S-i-j-T,费用为(-i-j),流量为1
你会发现是个二分图的模型。
如果流经过了i到达j的话,那么j也会显然选择i的,所以最后答案要除以2.
代码
#include<bits/stdc++.h>
using namespace std;
struct ZKW_MinCostMaxFlow{
const static int maxn = 1e5 + 50;
const static int maxE = 1e5 + 50;
const static int INF = 0x3f3f3f3f;
struct Edge{
int to,next,cap,flow,cost;
Edge(int _to=0,int _next=0,int _cap=0,int _flow=0,int _cost=0):
to(_to),next(_next),cap(_cap),flow(_flow),cost(_cost){}
}edge[maxE * 2];
int head[maxn],tot;
int cur[maxn];
int dis[maxn];
bool vis[maxn];
int ss,tt,N;
int min_cost,max_flow;
void init(int N){
tot=0;
for( int i = 0 ; i < N ; ++ i ) head[i] = -1;
}
int addedge(int u,int v,int cap,int cost){
edge[tot]=Edge(v,head[u],cap,0,cost);
int rs = tot;
head[u]=tot++;
edge[tot]=Edge(u,head[v],0,0,-cost);
head[v]=tot++;
return rs;
}
int aug(int u,int flow){
if(u==tt) return flow;
vis[u]=true;
for(int i=cur[u];i!=-1;i=edge[i].next){
int v=edge[i].to;
if( edge[i].cap>edge[i].flow && !vis[v] && dis[u]==dis[v]+edge[i].cost ){
int tmp=aug(v,min(flow,edge[i].cap-edge[i].flow));
edge[i].flow+=tmp;
edge[i^1].flow-=tmp;
cur[u]=i;
if(tmp) return tmp;
}
}
return 0;
}
bool modify_label(){
int d=INF;
for(int u=0;u<N;u++){
if(vis[u])
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].to;
if(edge[i].cap>edge[i].flow && !vis[v])
d=min(d,dis[v]+edge[i].cost-dis[u]);
}
}
if(d==INF) return false;
for(int i=0;i<N;i++)
if(vis[i]){
vis[i]=false;
dis[i]+=d;
}
return true;
}
pair < int , int > mincostmaxflow(int start,int ed,int n ){
ss=start,tt=ed,N=n;
min_cost=max_flow=0;
for(int i=0;i<n;i++) dis[i]=0;
while(1){
for(int i=0;i<n;i++) cur[i]=head[i];
while(1){
for(int i=0;i<n;i++) vis[i]=false;
int tmp=aug(ss,INF);
if(tmp==0) break;
max_flow+=tmp;
min_cost+=tmp*dis[ss];
}
if(!modify_label()) break;
}
return make_pair( max_flow , min_cost );
}
}solver;
int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
bool check(int a,int b){
if(a==b)return false;
if(a<b)swap(a,b);
int t = sqrt(a*a-b*b);
if(t*t!=a*a-b*b)return false;
if(gcd(b,t)!=1)return false;
return true;
}
int main(){
int a,b;
scanf("%d%d",&a,&b);
solver.init(b*2+5);
for(int i=a;i<=b;i++){
for(int j=a;j<=b;j++){
if(check(i,j)){
solver.addedge(i,j+b,1,-i-j);
}
}
solver.addedge(b*2+1,i,1,0);
solver.addedge(i+b,b*2+2,1,0);
}
pair<int,int>ans=solver.mincostmaxflow(b*2+1,b*2+2,b*2+5);
printf("%d %d\n",ans.first/2,-ans.second/2);
}