[Lydsy1805月赛]quailty 算法 BZOJ5362

分析:

题目中描述了一个二分图,让我们求最小权最大匹配,实际上其实是求n个点,在n*(n-1)/2中选n条边的权值和最小,形成一个每个点都有出边的体系,也就是基环树,(证明:因为我们需要二分图最大匹配,所以,我们手动模拟一下匈牙利算法发现,最大匹配一定是每个左端点连了一条边,最小权一定是每个左端点所能连的最小边权,之后将二分图压缩成一个图,其实就是每一个点连了一条边使权值和最小。并且可以发现,如果满足这个性质,最少需要3个点联通才可以。),并且每个边的边权是对应的边的两个端点的权值抑或和,在这种情况下,贪心很显然,(疑惑法则,最大位一定相同。)那么我们考虑每次分治处理两个最大位相同的部分,之后合并,如果两个联通块的大小同时>3,那么就不需要合并,如果同时大于2,那么需要合并一次,否则,合并两次。时间复杂度:O(nlogn+3*n)

在分治之前,我们必须先排一下序,不然时间复杂度就退化为O(nsqrt(n)logn)了

这个题比较神,很有趣,想了一个晚上,早上突然就会了...

附上代码:

#include <cstdio>
#include <iostream>
#include <queue>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 300005
#define ll long long
int n,a[N],T;ll ans;
void solve(int x,int l,int r)
{
    if((x==-1)||(r==l))return ;
    if(r-l==1){ans+=a[l]^a[r];return;}
    int m=l;
    while((m<=r)&&!((a[m]>>x)&1))m++;
    if(m!=l)solve(x-1,l,m-1);
    if(m<=r)solve(x-1,m,r);
    if(m==l||m>r||(m-l>=3&&r-m+1>=3))return ;
    int minn=1<<30,minx=1<<30;
    for(int i=l;i<m;i++)
    {
        for(int j=m;j<=r;j++)
        {
            if((a[i]^a[j])<minn)minx=minn,minn=a[i]^a[j];
            else if((a[i]^a[j])<minx)minx=a[i]^a[j];
        }
    }
    if(m-l<=2&&r-m+1<=2)ans+=minn+minx;
    else ans+=minn;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);ans=0;
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        sort(a+1,a+n+1);solve(30,1,n);printf("%lld\n",ans);
    }
    return 0;
}

  

posted @ 2018-06-03 11:29  Winniechen  阅读(336)  评论(0编辑  收藏  举报