HDMI 数据包简析

P5662 [CSP-J2019] 纪念品题解

  返回  

可持久化Trie+堆优化 OR Trie树上求XOR第K大 ---- P5283 [十二省联考2019]异或粽子

2021/8/21 19:16:00 浏览:

题目大意


题目大意:

在这里插入图片描述


  1. 考虑先做个 prefix xor 前缀异或 b i = ⨁ j = 1 i a j ( 1 ≤ i ≤ n ) b_i=\bigoplus_{j=1}^{i}a_j(1\leq i \leq n) bi=j=1iaj(1in)
  2. 那么就可以 O ( 1 ) O(1) O(1)查询了
  3. ⨁ i = l r a i = b r ⊕ b l − 1 \bigoplus_{i=l}^{r}a_i=b_r\oplus b_{l-1} i=lrai=brbl1
  4. 然后考虑这样我们要解决的问题就变成了给定 n n n 个数,求异或值最靠前的 k k k 对。

解法1:可持久化Trie+堆优化

首先为了保证不会算重复就是因为 a j ⊕ a i = a i ⊕ a j a_j\oplus a_i=a_i\oplus a_j ajai=aiaj

我们先对每个 a i a_i ai [ 1 , i ] [1,i] [1,i]求一个异或最大值并且把这 n n n个数加入堆中。

然后每次从堆里面取出最大值,假如这次取出去的值是在 [ l , r ] [l,r] [l,r]里面的,异或最大值的位置是 k k k那么异或次大值肯定是在 [ l , k − 1 ] [l,k-1] [l,k1] [ k + 1 , r ] [k+1,r] [k+1,r]里面又放到堆里面


code 1:

#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define f first
#define s second
#define endl '\n'
using namespace std;
const int N = 2e6 + 10, mod = 1e9 + 9;
const int maxn = 500010;
const long double eps = 1e-5;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x) {
   x = 0;char ch = getchar();ll f = 1;
   while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
   while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) {
   read(first);
   read(args...);
}
ll arr[maxn];
struct Trie {
    int tr[maxn * 40][2], sum[maxn *40], id[maxn * 40];
    int root[maxn], cnt; 
    inline void insert(int &now, int pre, int val, int idx) {
        now = ++ cnt;
        int poi = now;
        for(int i = 31; i >= 0; -- i) {
            tr[poi][0] = tr[pre][0];
            tr[poi][1] = tr[pre][1];
            sum[poi] = sum[pre] + 1;
            int c = (val >> i) & 1;
            pre = tr[pre][c];
            tr[poi][c] = ++ cnt;
            poi = tr[poi][c];
        }
        sum[poi] = sum[pre] + 1;
        id[poi] = idx;
    }
    inline int query(int rtl, int rtr, int val) {
        for(int i = 31; i >= 0; -- i) {
            int c = (val >> i) & 1;
            if((sum[tr[rtr][c^1]] - sum[tr[rtl][c^1]]) > 0) {
                rtl = tr[rtl][c^1];
                rtr = tr[rtr][c^1];
            } else {
                rtl = tr[rtl][c];
                rtr = tr[rtr][c];                
            }
        };
        return id[rtr];//返回和val异或最大那个数的下标
    }
} trie;

struct node {
    int l, r, id, i;
    ll val;
    bool operator < (const node & a) const {
        return val < a.val;
    }
};
priority_queue <node> q;
int main() {
    IOS;
    int n, m;
    cin >> n >> m;
    trie.insert(trie.root[1],trie.root[0],0,1);// 处理边界是1 arr[1] = 0;
    for(int i = 2; i <= n + 1; ++ i) {
        cin >> arr[i];
        arr[i] ^= arr[i-1];
        trie.insert(trie.root[i],trie.root[i-1],arr[i],i);
    }
    for(int i = 2; i <= n + 1; ++ i) {
        int id = trie.query(trie.root[0],trie.root[i],arr[i]);
        ll val = arr[id] ^ arr[i];
        q.push((node){0,i,id,i,val});
    }
    ll ans = 0;
    for(int i = 1; i <= m; ++ i) {
        auto top = q.top();
        q.pop();
        ans += top.val;
        int l = top.l, r = top.r, id = top.id, idx = top.i; // id是最大数的下标
        if(l+1 < id) {
           int idl = trie.query(trie.root[l],trie.root[id-1],arr[idx]); // 查询[l,id-1]
           ll vall = arr[idl] ^ arr[idx];
           q.push((node){l,id-1,idl,idx,vall});
        }
        if(id < r) {
            int idr = trie.query(trie.root[id],trie.root[r],arr[idx]);//查询(id,r] = [id+1,r] 左开右闭
            ll valr = arr[idr] ^ arr[idx];
            q.push((node){id,r,idr,idx,valr});
        }
    }
    cout << ans;
    return 0;
}

解法2:

就是直接在Trie树上求Xor第K大就可以了
在这里插入图片描述


code 2

#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define f first
#define s second
#define endl '\n'
using namespace std;
const int N = 2e6 + 10, mod = 1e9 + 9;
const int maxn = 500010;
const long double eps = 1e-5;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x) {
   x = 0;char ch = getchar();ll f = 1;
   while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
   while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args)  {
    read(first);
    read(args...);
}
int tr[maxn * 40][2], cnt;
int sum[maxn * 40];
ll arr[maxn];
struct node {
    int rk, i;
    ll val;
    bool operator < (const node & a) const {
        return val < a.val;
    } 
};
priority_queue<node> q;
inline void insert(ll x) {
    int rt = 0;
    for(ll i = 31; i >= 0; -- i) {
        int c = (x >> i) & 1;
        if(!tr[rt][c]) tr[rt][c] = ++ cnt;
        sum[rt] ++;
        rt = tr[rt][c];
    }
    sum[rt] ++;
    return;
}

inline ll ask(ll x, int kth) {
    int rt = 0;
    ll ans = 0;
    for(ll i = 31; i >= 0; -- i) {
        int c = (x >> i) & 1;
        if(!tr[rt][c^1]) rt = tr[rt][c];
        else if(sum[tr[rt][c^1]] < kth) kth -= sum[tr[rt][c^1]], rt = tr[rt][c]; // 类似权值线段树的跳法
        else rt = tr[rt][c^1], ans |= (1ll << i);
    }
    return ans;
}

int main() {
    //IOS;
    ll n, m;
    read(n,m);
    insert(0);
    for(int i = 1; i <= n; ++ i) {
        cin >> arr[i];
        arr[i]  ^= arr[i-1];
        insert(arr[i]);
    }
    for(int i = 0; i <= n; ++ i) q.push({1,i,ask(arr[i],1)});
    ll ans = 0;
    for(int i = 1; i <= 2 * m; ++ i) {
        auto top = q.top();
        q.pop();
        ans += top.val;
        if(top.rk < n) q.push({top.rk+1,top.i,ask(arr[top.i],top.rk+1)});
    }
    cout << (ans >> 1);
    return 0;
}

联系我们

如果您对我们的服务有兴趣,请及时和我们联系!

服务热线:18288888888
座机:18288888888
传真:
邮箱:888888@qq.com
地址:郑州市文化路红专路93号