51nod 1397

13 篇文章 0 订阅
11 篇文章 0 订阅

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

我们先把图中的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 long
using 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值