• cf 888G


    题目链接:传送门

    题目思路:

    对于ai ,找到一个aj 满足其二进制的公共前缀最长(公共前缀越长,异或值越小),如果aj有多个,那么再枚举判断和谁连边是最优的。

    对于本题的做法,可以采用针对第k位的0/1进行分治(把区间按第k位的分成两个子区间),这样能保证每个ai会和另一个公共前缀最长的aj连边。回溯时,是两个连通块(单个点也可视作连通块)进行合并(两个连通块的公共前缀也是最长的),如果直接暴力合并,那么每一层分治的总复杂度是O(n2) 的,显然不行, 这里可以把一个连通块的所有点都放到一棵字典树里(字典树维护二进制),再用另一个连通块的点依次在字典树里查询(从高位到低位,尽量沿着与ai 的某一位相同的 0/1 方向查找),详细可以参考代码。

    代码:

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long LL;
     4 typedef unsigned long long uLL;
     5 typedef pair<int,int> pii;
     6 typedef pair<LL,LL> pLL;
     7 typedef pair<double,double> pdd;
     8 const int N=2e5+5;
     9 const int M=1e7+5;
    10 const int inf=0x3f3f3f3f;
    11 const LL mod=1e9+7;
    12 const double eps=1e-8;
    13 const long double pi=acos(-1.0L);
    14 #define ls (i<<1)
    15 #define rs (i<<1|1)
    16 #define fi first
    17 #define se second
    18 #define pb push_back
    19 #define eb emplace_back
    20 #define mk make_pair
    21 #define mem(a,b) memset(a,b,sizeof(a))
    22 LL read()
    23 {
    24     LL x=0,t=1;
    25     char ch;
    26     while(!isdigit(ch=getchar())) if(ch=='-') t=-1;
    27     while(isdigit(ch)){ x=10*x+ch-'0'; ch=getchar(); }
    28     return x*t;
    29 }
    30 int bt[N<<5][2],a[N],cnt;
    31 LL ans;
    32 void ins(int x)
    33 {
    34     int p=1;
    35     for(int i=29;i>=0;i--)
    36     {
    37         int t=((1<<i)&x)>0;
    38         if(!bt[p][t]) bt[p][t]=++cnt,bt[cnt][0]=bt[cnt][1]=0;
    39         p=bt[p][t];
    40     }
    41 }
    42 void cal(int l,int mid,int r)
    43 {
    44     bt[cnt=1][0]=bt[1][1]=0;
    45     for(int i=l;i<mid;i++) ins(a[i]);
    46     LL res=1e12;
    47     for(int i=mid;i<=r;i++)
    48     {
    49         int p=1;
    50         LL sum=0;
    51         for(int j=29;j>=0;j--)
    52         {
    53             int x=(a[i]>>j)&1;
    54             if(!bt[p][x]) sum+=1<<j,x^=1;
    55             p=bt[p][x];
    56         }
    57         res=min(res,sum);
    58     }
    59     ans+=res;
    60 }
    61 void dfs(int l,int r,int k)
    62 {
    63     if(k==-1) return ;
    64    // printf("%d , %d , %d
    ",l,r,k);
    65     int mid=0;
    66     for(int i=l;i<=r&&!mid;i++)
    67         if((1<<k)&a[i]) mid=i;
    68     if(mid==0) mid=r+1;
    69     if(l<mid) dfs(l,mid-1,k-1);
    70     if(mid<=r) dfs(mid,r,k-1);
    71     if(l<mid&&mid<=r) cal(l,mid,r);
    72 }
    73 int main()
    74 {
    75     int n=read();
    76     for(int i=1;i<=n;i++) a[i]=read();
    77     sort(a+1,a+n+1);
    78     dfs(1,n,29);
    79     printf("%lld
    ",ans);
    80     return 0;
    81 }
    82 /*
    83 4
    84 1 2 3 4
    85 */
    View Code
  • 相关阅读:
    SQL Server中使用convert进行日期转换
    杂记
    sqlserver表分区与调优与行列转换
    HttpModule的认识与深入理解及MVC运行机制
    再谈委托
    ASP.NET forms凭据设置和跳转的几种方法
    IOS学习网址
    Activator.CreateInstance 方法 (Type) 的用法
    update多表更新的2种方式
    SQL自定义函数split分隔字符串
  • 原文地址:https://www.cnblogs.com/DeepJay/p/13995796.html
Copyright © 2020-2023  润新知