BZOJ5362: [Lydsy1805月賽]quailty 算法
阿新 • • 發佈:2018-12-24
最小 .com %d pri def string define ans scan
BZOJ5362: [Lydsy1805月賽]quailty 算法
https://lydsy.com/JudgeOnline/problem.php?id=5362
分析:
- 題意即求一個最小基環樹森林,兩點之間邊權為異或值。
- 這題的思路很好,先排序,我們二進制分組,將\(0\)和\(1\)分成兩部分,顯然這兩部分之間的邊能不連就不連。
- 但也有必須連的情況,就是出現某個集合大小小於等於\(2\)的情況,內部無法自身構成基環樹,需要和另外一個集合連邊,此時我們暴力找最小的邊即可。
- 時間復雜度為\(O(nlogn)\)。
代碼:
#include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std; #define N 300050 typedef long long ll; int n,a[N],k=30; ll ans; void solve(int l,int r,int d) { if(d==-1||l>=r) return ; if(l==r-1) {ans+=a[l]^a[r]; return ;} int mid=l,i,j; for(;mid<=r&&!((a[mid]>>d)&1);mid++) ; solve(l,mid-1,d-1); solve(mid,r,d-1); int ls=mid-l,rs=r-mid+1; if(ls>2&&rs>2) return ; if(ls<1||rs<1) return ; int mn1=1<<30,mn2=1<<30; for(i=l;i<mid;i++) { for(j=mid;j<=r;j++) { int x=a[i]^a[j]; if(x<mn1) mn2=mn1,mn1=x; else if(x<mn2) mn2=x; } } if(ls<=2&&rs<=2) ans+=mn1+mn2; else ans+=mn1; } int main() { int T; scanf("%d",&T); while(T--) { scanf("%d",&n); int i; for(i=1;i<=n;i++) scanf("%d",&a[i]); sort(a+1,a+n+1); ans=0; solve(1,n,k); printf("%lld\n",ans); } }
BZOJ5362: [Lydsy1805月賽]quailty 算法