分析:
题目中描述了一个二分图,让我们求最小权最大匹配,实际上其实是求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 ",ans); } return 0; }