[bzoj3166][HEOI2013]ALO

来源:互联网 发布:淘宝口令是什么意思啊 编辑:程序博客网 时间:2024/06/14 15:21

题目大意

现有一个序列,一段长度>1区间的权值为区间内的次大值与区间内除次大值外的数的异或最大值。
例如:9 2 1 4 7
次大值为7,7 xor 9=14最大。
保证序列内元素两两不同。N<=50000,每个元素都不超过10^9.

解决次大值

我们考虑枚举一个数作为次大值,然后求出其最大往左和最大往右,那么显然,需要在这个区间内寻找与其异或最大的数。注意序列内的最大值永远不可能作为次大值。
我们考虑从大到小排序,顺次插入线段树中。对于现在要插入的原来在第i位的元素,j为在1~i-1区间中最右元素所在位置,k为在1~j-1区间中最右元素所在位置。显然k+1~i这个区间的次大值为i。往右扩展情况类似。因此我们可以用n log n的时间处理出扩展情况。

异或最大值

为了方便,我们可以把一个元素的扩展情况分成两块,一块是往左扩展情况,另一块是往右扩展情况。
对于往左扩展情况,从左到右做。当前做到i,其往左最大扩展到j。那么我们需要在j+1~i-1中寻找最大异或数。我们可以想到贪心,因为所有元素不超过10^9,所以化为二进制最多30位,将前i-1个元素放入trie中,每次尽量走能使当前位异或后为1的那一侧,即可。
但注意我们需满足元素位置号>j,因此在trie中每个结点维护一个最大标号。只要最大标号>j,则这条路一定可以走下去。
再类似的倒过来做一遍。

参考程序

#include<cstdio>#include<algorithm>#define fo(i,a,b) for(i=a;i<=b;i++)#define fd(i,a,b) for(i=a;i>=b;i--)using namespace std;typedef long long ll;struct dong{    int x,id;};const int maxn=50000+10,ws=30;int tree[maxn*33][2],g[maxn*33],num[maxn*5],sum[maxn*5];int a[maxn][33],b[maxn],s[maxn],ans[maxn],pre[maxn],suf[maxn];dong c[maxn];int i,j,k,l,r,t,n,m,tot,top;void insert(int p,int t,int i){    g[p]=i;    if (t<0) return;    if (!tree[p][a[i][t]]) tree[p][a[i][t]]=++tot;    insert(tree[p][a[i][t]],t-1,i);}int find(int p,int t,int i,int j,int flag){    if (t<0) return 0;    if (!flag){        if (tree[p][1-a[i][t]]&&g[tree[p][1-a[i][t]]]>j) return find(tree[p][1-a[i][t]],t-1,i,j,flag)+(1<<t)*(1-a[i][t]);        else return find(tree[p][a[i][t]],t-1,i,j,flag)+(1<<t)*a[i][t];    }    else{        if (tree[p][1-a[i][t]]&&g[tree[p][1-a[i][t]]]<j) return find(tree[p][1-a[i][t]],t-1,i,j,flag)+(1<<t)*(1-a[i][t]);        else return find(tree[p][a[i][t]],t-1,i,j,flag)+(1<<t)*a[i][t];    }}bool cmp(dong a,dong b){    return a.x>b.x;}void change(int p,int l,int r,int a){    if (l==r){        sum[p]=num[p]=l;        return;    }    int mid=(l+r)/2;    if (a<=mid) change(p*2,l,mid,a);else change(p*2+1,mid+1,r,a);    num[p]=max(num[p*2],num[p*2+1]);    sum[p]=min(sum[p*2],sum[p*2+1]);}int get(int p,int l,int r,int a,int b,int flag){    if (l==a&&r==b){        if (flag) return num[p];else return sum[p];    }    int mid=(l+r)/2;    if (b<=mid) return get(p*2,l,mid,a,b,flag);    else if (a>mid) return get(p*2+1,mid+1,r,a,b,flag);    else{        int j=get(p*2,l,mid,a,mid,flag),k=get(p*2+1,mid+1,r,mid+1,b,flag);        if (flag) return max(j,k);else return min(j,k);    }}int main(){    scanf("%d",&n);    fo(i,1,n){        j=0;        scanf("%d",&t);        b[i]=t;        while (t){            a[i][j]=t%2;            t/=2;            j++;        }        c[i].x=b[i];c[i].id=i;    }    sort(c+1,c+n+1,cmp);    fo(i,1,4*n) sum[i]=n+1;    fo(i,1,n){        j=get(1,1,n,1,c[i].id,1);        if (!j) pre[c[i].id]=0;        else{            if (j>1){                k=get(1,1,n,1,j-1,1);                j=k+1;            }            pre[c[i].id]=j;        }        j=get(1,1,n,c[i].id,n,0);        if (j==n+1) suf[c[i].id]=n+1;        else{            if (j<n){                k=get(1,1,n,j+1,n,0);                j=k-1;            }            suf[c[i].id]=j;        }        change(1,1,n,c[i].id);    }    fo(i,1,n){        k=find(0,ws,i,pre[i]-1,0);        ans[i]=b[i]^k;        insert(0,ws,i);    }    fo(i,0,tot) g[i]=n+1,tree[i][0]=tree[i][1]=0;    fd(i,n,1){        k=find(0,ws,i,suf[i]+1,1);        ans[i]=max(ans[i],b[i]^k);        insert(0,ws,i);    }    m=0;    l=r=0;    fo(i,1,n){        if (pre[i]==0&&suf[i]==n+1) ans[i]=0;        if (ans[i]>m){            m=ans[i];            l=pre[i];r=suf[i];        }    }    printf("%d\n",m);    return 0;}
0 0
原创粉丝点击