【HBOI2013】ALO

題目

Welcome to ALO ( Arithmetic and Logistic Online)。這是一個VR MMORPG,如名字所見,到處充滿了數學的謎題。

現在你擁有n顆寶石,每顆寶石有一個能量密度,記爲ai,這些寶石的能量密度兩兩不同。現在你可以選取連續的一些寶石(必須多於一個)進行融合,設爲ai, ai+1, …, aj,則融合而成的寶石的能量密度爲這些寶石中能量密度的次大值與其他任意一顆寶石的能量密度按位異或的值的最大值,即,設該段寶石能量密度次大值爲k,則生成的寶石的能量密度爲max{k xor ap | ap ≠ k , i ≤ p ≤ j}。

現在你需要知道你怎麼選取需要融合的寶石,才能使生成的寶石能量密度最大。

對於20%的數據有n ≤ 100。

對於50%的數據有n ≤ 2000。

對於100%的數據有1 ≤ n ≤ 50000, 0 ≤ ai ≤ 10^9。

分析

我們可以把題目要求的東西分成兩個部分,
次小值,和最大異或值。
我們逐步考慮。
次小值我們可以從頭枚舉一個數ai,然後用線段樹求出左邊第1,2個比ai大的數,
和右邊第1,2個比ai大的數,分別爲l1,l2,r1,r2.
那麼我們要求的便是l1+1..r2-1之間與ai的最大異或值。
之前做過一題,就是求最大異或值。
不過之前只用詢問一次
做法便是把所有的數轉成二進制後放進二叉樹中(當然是從2^n…2^0這樣的順序放,因爲這樣方便我們貪心)
然後再從根開始,每次儘量選與枚舉的數的這個位不同的數,即可。
那麼現在變成了很多個詢問,新的知識出現了——可持續化字典樹!
其實也就是主席樹的思想


trie[x][0..1]表示這個節點的0..1編號
sum[x][0..1]表示這個節點的0..1出現的次數。

for(int i=1;i<=n;i++) root[i]=add(root[i-1],a[i].val);
//建樹,從上一個節點開始建起,因爲這次與上次會有交集,故可以從上次建起

int add(int x,int vale){
    int temp,y;temp=y=cnt++;
    for(int i=30;i>=0;i--){//倒序是爲了保證貪心正確性
        int t=(vale&(1<<i))>>i;//t表示vale的第i位爲多少
        trie[y][0]=trie[x][0];
        trie[y][1]=trie[x][1];
        x=trie[x][t];
        y=trie[y][t]=cnt++;
        sum[y]=sum[x]+1;
    }
    return temp;
}

下面的是查詢操作,其實也類似
int query(int u,int x,int y){
    int temp=0;
    for(int i=30;i>=0;i--){
        int t=(u&(1<<i))>>i;
        if (sum[trie[y][t^1]]-sum[trie[x][t^1]]) x=trie[x][t^1],y=trie[y][t^1],temp+=1<<i;
        else x=trie[x][t],y=trie[y][t];
    }
    return temp;
}

完整代碼:

#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#define maxlongint 2147483647
using namespace std;
const int N=50000;
struct note{
    long long val,fin;
}a[N],b[N];
struct pe{
    int ma,mi;
}tree[N*4];
bool cmp(note a,note b){
    return a.val>b.val;
}
int n,l,r,l1,r1,root[N*35],trie[N*35][2],sum[N*35],cnt;
int ans;
void make(int k){
        tree[k].ma=max(tree[k*2+1].ma,tree[k*2].ma);
        tree[k].mi=min(tree[k*2+1].mi,tree[k*2].mi);
}
void insert(int k,int l,int r,int z){
    if (l==r) {
        tree[k].ma=tree[k].mi=z;
    } else {
        int mid=(l+r)>>1;
        if (mid>=z) insert(k*2,l,mid,z);else insert(k*2+1,mid+1,r,z);
        make(k);
    }
}
void find(int k,int l,int r,int x,int y,int &z,int le){
    if (l==x&&r==y){
        if (le) z=max(z,tree[k].ma);else z=min(z,tree[k].mi);
    } else {
        int mid=(l+r)>>1;
        if (mid>=y) find(k*2,l,mid,x,y,z,le);
        else if (mid<x) find(k*2+1,mid+1,r,x,y,z,le);
        else {
            find(k*2,l,mid,x,mid,z,le);find(k*2+1,mid+1,r,mid+1,y,z,le);
        }
    }
}
int add(int x,int vale){
    int temp,y;temp=y=cnt++;
    for(int i=30;i>=0;i--){
        int t=(vale&(1<<i))>>i;
        trie[y][0]=trie[x][0];
        trie[y][1]=trie[x][1];
        x=trie[x][t];
        y=trie[y][t]=cnt++;
        sum[y]=sum[x]+1;
    }
    return temp;
}
int query(int u,int x,int y){
    int temp=0;
    for(int i=30;i>=0;i--){
        int t=(u&(1<<i))>>i;
        if (sum[trie[y][t^1]]-sum[trie[x][t^1]]) x=trie[x][t^1],y=trie[y][t^1],temp+=1<<i;
        else x=trie[x][t],y=trie[y][t];
    }
    return temp;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=N*4;i++) tree[i].mi=maxlongint;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i].val);
        a[i].fin=i;
        b[i].val=a[i].val;b[i].fin=a[i].fin;
    }
    for(int i=1;i<=n;i++) root[i]=add(root[i-1],a[i].val);
    sort(a+1,a+n+1,cmp);
    insert(1,1,n,a[1].fin);
    ans=0;
    for(int i=2;i<=n;i++){
        l=0;r=maxlongint;l1=0;r1=maxlongint;
        find(1,1,n,1,a[i].fin,l,1);find(1,1,n,a[i].fin,n,r,0);
        if (l>1) find(1,1,n,1,l-1,l1,1);
        if (r<n) find(1,1,n,r+1,n,r1,0);
        if (l1||r1!=maxlongint){
        if (!l1) l1=1;if (r1==maxlongint) r1=n;
        ans=max(ans,query(a[i].val,root[l1],root[r1-1]));
        }
        insert(1,1,n,a[i].fin);
    }
    printf("%d",ans);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章