51nod 1397

来源:互联网 发布:中国调查数据网 编辑:程序博客网 时间:2024/06/09 15:26

根据增广路的性质(未匹配匹配未匹配交错路),画图贪贪贪感觉能搞出来?

我们先把图中的m对匹配点连上,得到这个东西这里写图片描述蓝色的为匹配边,上方为集合A,下方为集合B,不妨设|B|>=|A|
先不考虑匹配点的度数够不够,我们尝试补并尽量添加非匹配点的度
因为m个匹配已经存在,所以A集和B集中的非匹配点之间不能有边
我们先为B集某个未匹配点添加边(这里假定度数要求为2,所以这个未匹配点至少要添加2条边,与匹配集合中的2对匹配有直接联系)这里写图片描述 红线为未匹配边
考虑到交错路的性质,从这个点走未匹配边走进匹配集合内,马上走一条匹配边(就往下走走回了B集合),接下来每次走完一次未匹配边(到A),如果仍在匹配集合内就会往下走,直到不匹配,就产生了增广路
我们不容许这种增广路的产生,所以与这个点相连的这两个对点不能和A集未匹配点再有边,既然他们已经和A集未匹配点独立开来,在这里面走也走不出去,不妨让B集剩余的点也都连上这个集合这里写图片描述
然后这个2对点内部显然可以随便加边(同时保证了匹配点的度数>=d),同时他们在A集合中的2个匹配点可以向B中的其他匹配点也连边因为B中的未匹配点一走到A就一定沿着匹配边走下去,所以再连边不影响这里写图片描述
于是我们就得到了一个保证合法的策略,将这m对匹配分成若干个集合,每个集合分配给A或者B,然后连连连
容易发现集合显然分的越少越好( (x+y)^2+n1*(x+y)>=x^2+y^2+n1*(x+y) )
于是就将m个点分成2个,一个给A中的未匹配点一个给B,可以列出一个二次开口向上的函数,变成了一个中学生数学题

要注意|A|=m的情况,这种情况我们这种分法并不适用事实上也不需要分,直接全部连在一起就行了
再判一下合法性就ok了

code:

#include<set>#include<map>#include<deque>#include<queue>#include<stack>#include<cmath>#include<ctime>#include<bitset>#include<string>#include<vector>#include<cstdio>#include<cstdlib>#include<cstring>#include<climits>#include<complex>#include<iostream>#include<algorithm>#define ll long longusing namespace std;ll n1,n2,m,d;ll sqr(const ll x){return x*x;}int main(){    int tcase; scanf("%d",&tcase);    while(tcase--)    {        scanf("%lld%lld%lld%lld",&n1,&n2,&m,&d);        if(n1<m||n2<m) { puts("-1");continue; }        if(min(n1,n2)==m) { printf("%lld\n",d>m?-1:n1*n2);continue; }        if(m<2*d) { puts("-1"); continue; }        n1-=m,n2-=m;        double mid=(double)m/2.0;        double x0=(double)(m-n1+n2)/2.0;        ll x=x0<=mid?m-d:d;        printf("%lld\n",sqr(x)+(n1-n2-m)*x+sqr(m)+n2*m);    }    return 0;}