01字典树逆应用+树上dp——cf1285D

不知道为啥只开int会炸。。这题思维过程还是很有趣(痛苦)的

/*
01字典树求X异或最大值的策略就是X取反后去字典树里跑,跑到不能跑了就是答案 
    X在往01trie下搜时,如果碰到要去的地方是null,那么它只能朝另一个方向走,
    同时最终异或起来的答案(即和X异或最大的那个值)损失了1<<(29-i) 
    本题就是要让这个损失的值达到最大
*/
#include
using namespace std;
#define ll long long 
#define N 200005
#define ll long long
ll n,a[N];
//root=0
struct Trie{
    ll nxt[N*32][2],L,root,pow[N*32],sum[N*32],id[N*32];
    int newnode(){
        nxt[L][0]=nxt[L][1]=-1;
        return L++;
    }
    inline void init(){
        L=0;
        root=newnode();
        pow[root]=1<<29;
    }
    void insert(int x,int idx){
        int now=root;
        for(int i=29;i>=0;i--){
            int k=(x>>i) & 1;
            if(nxt[now][k]==-1)
                nxt[now][k]=newnode();
            now=nxt[now][k];
        }
        id[now]=idx;
    }
    void solve(){
        queue<int>q;q.push(root);
        while(q.size()){
            int now=q.front();q.pop();
            for(int i=0;i<2;i++)
                if(nxt[now][i]!=-1){
                    pow[nxt[now][i]]=pow[now]/2;
                    q.push(nxt[now][i]);
                }
        }
        q.push(root);
        while(q.size()){
            int now=q.front();q.pop();
            int flag=(nxt[now][0]!=-1) && (nxt[now][1]!=-1);
            for(int i=0;i<2;i++)
                if(nxt[now][i]!=-1){
                    sum[nxt[now][i]]=sum[now];
                    if(flag)
                        sum[nxt[now][i]]+=pow[now];    
                    q.push(nxt[now][i]);
                }
        }
        ll Min=0x3f3f3f3f3f3f3f3f;
        for(int i=0;i)
            if(id[i]) Min=min(Min,sum[i]);
        cout<'\n';
    }
}tr;

int main(){
    cin>>n;
    tr.init();
    for(int i=1;i<=n;i++){
        cin>>a[i];
        tr.insert(a[i],i);
    }
    tr.solve(); 
} 

 

你可能感兴趣的:(01字典树逆应用+树上dp——cf1285D)