01trie

貌似可以解决一些跟异或相关的问题?

 

POJ - 3764 

 1 #include<vector>
 2 #include<cstdio>
 3 #include<iostream>
 4 #define pb push_back
 5 #define nc getchar
 6 #define read(x) scanf("%d", &x)
 7 using namespace std;
 8 struct Edge{int v, w, n;} E[200005];
 9 int n, d[100005], head[100005], ec;
10 inline void aE(int u, int v, int w) {
11     E[++ec] = (Edge){v, w, head[u]}; head[u] = ec;
12     E[++ec] = (Edge){u, w, head[v]}; head[v] = ec;
13 }
14 void dfs(int u, int f, int w) {
15     d[u] = w; for (int i = head[u]; i; i = E[i].n)
16         if (E[i].v != f) dfs(E[i].v, u, E[i].w ^ w);
17 }
18 struct Node {
19     Node *ch[2];
20 } Tnull, *null = &Tnull, *root;
21 Node mem[2000000], *C;
22 inline Node* newNode() {
23     C->ch[0] = C->ch[1] = null; return C++;    
24 }
25 void insert(int x) {
26     Node *u = root;
27     for (int i = 31; ~i; --i) {
28         int c = (x >> i) & 1;
29         if (u->ch[c] == null) u->ch[c] = newNode();
30         u = u->ch[c];
31     }
32 }
33 int find(int x) {
34     Node *u = root; int res = 0;
35     for (int i = 31; ~i; --i) {
36         int c = !((x >> i) & 1);
37         if (u->ch[c] == null) u = u->ch[!c];
38         else u = u->ch[c], res += 1 << i;
39     } return res;
40 }
41 void solve() {
42     for (int i = 1; i <= n; ++i) insert(d[i]);    int res = 0;
43     for (int i = 1; i <= n; ++i) res = max(res, find(d[i]));
44     printf("%d\n", res);
45 }
46 int main() {
47     while (~scanf("%d", &n)) {
48         for (int u, v, w, i = 1; i < n; ++i)
49             read(u), read(v), read(w), aE(u + 1, v + 1, w);
50         for (int i = head[1]; i; i = E[i].n) dfs(E[i].v, 1, E[i].w);
51         C = mem; root = newNode(); solve();
52         for (int i = 1; i <= n; ++i) d[i] = 0;
53         for (int i = 1; i <= n; ++i) head[i] = 0;
54         ec = 0;
55     }
56     return 0;    
57 }
View Code

这题咱用vector存边T了。

记f(i)为从根节点出发到i点时边权的异或和。

根据异或的性质,u-v这条路径的异或和就是f(u) xor f(v)。

把所有的f(i)都插入一个01trie上,然后枚举一个f(i),再贪心另一个。

 

BZOJ - 3261

#include<cstdio>
#include<iostream>
using namespace std;
inline char nc() {
    static char b[1<<12],*s=b,*t=b;    
    return s==t&&(t=(s=b)+fread(b,1,1<<12,stdin),s==t)?-1:*s++;
}
inline void read(int &x) {
    char b = nc(); x = 0;
    for (; !isdigit(b); b = nc());
    for (; isdigit(b); b = nc()) x = x * 10 + b - '0';
}
inline int read() {
    char b = nc();
    for (; !isalpha(b); b = nc());
    return b == 'A';
}
struct Node {
    Node *ch[2]; int v;    
} Tnull, *null = &Tnull;
Node mem[24000010], *rt[600010], *C = mem;
inline Node* newNode() {
    C->ch[0] = C->ch[1] = null;    C->v = 0; return C++;
}
int n, m, xr;
void insert(int x, Node *u, Node *p) {
    for (int i = 24; ~i; --i) {
        int c = (x >> i) & 1;
        if (u->ch[c] == null) u->ch[c] = newNode();
        u->ch[!c] = p->ch[!c];
        u = u->ch[c]; p = p->ch[c];
        u->v = p->v + 1;
    }
}
int query(int x, Node *L, Node *R) {
    int res = 0;
    for (int i = 24; ~i; --i) {
        int c = !((x >> i) & 1);
        if (R->ch[c]->v - L->ch[c]->v > 0) res += 1 << i; else c = !c;
//        R->ch[c]->v - L->ch[c]->v > 0 ? res += 1 << i : c = !c;
        R = R->ch[c], L = L->ch[c];
    } return res;
}
int main() {
    null->ch[0] = null->ch[1] = null;
    read(n); read(m); ++n;
    rt[0] = newNode(); rt[1] = newNode(); insert(0, rt[1], rt[0]);
    for (int i = 2, t; i <= n; ++i) 
        read(t), xr ^= t, rt[i] = newNode(), insert(xr, rt[i], rt[i-1]);
    for (int x, l, r, i = 0; i < m; ++i) {
        if (read()) {
            read(x); xr ^= x; ++n; rt[n] = newNode(); insert(xr, rt[n], rt[n-1]);
        } else {
            read(l); read(r); read(x);
            printf("%d\n", query(xr ^ x, rt[l-1], rt[r]));
        }
    }    
    return 0;
}
View Code

记 $f_n = a_1 \; xor \; a_2 \; xor \; ... \; xor \; a_n$

即求 $x \; xor \; f_n \; xor \; f_{p-1}$ 的最大值。

注意到 $x \; xor \; f_n$ 为一定值,所以我们只需要在只存$[l-1, r-1]$这段区间值的trie上贪心就行。

 

BZOJ - 3689

 1 #include<queue>
 2 #include<cstdio>
 3 #include<iostream>
 4 #define nc getchar
 5 using namespace std;
 6 inline void read(int &x) {
 7     char b = nc(); x = 0;
 8     for (; !isdigit(b); b = nc());
 9     for (; isdigit(b); b = nc()) x = x * 10 + b - '0';
10 }
11 struct Node {
12     Node *ch[2]; int sz;
13 } Tnull, *null = &Tnull, *root;
14 Node mem[3200000], *C = mem;
15 inline Node* newNode() {
16     C->sz = 0; C->ch[0] = C->ch[1] = null; return C++;
17 }
18 void insert(int x) {
19     Node *u = root; ++u->sz;
20     for (int i = 30; ~i; --i) {
21         int c = (x >> i) & 1;
22         if (u->ch[c] == null) u->ch[c] = newNode();
23         u = u->ch[c]; ++u->sz;
24     }
25 }
26 int query(int x, int k) {
27     Node *u = root; int res = 0;
28     for (int i = 30; ~i; --i) {
29         int c = (x >> i) & 1;
30         if (k > u->ch[c]->sz) 
31             res |= 1 << i, k -= u->ch[c]->sz, c = !c;
32         u = u->ch[c];
33     } return res;
34 }
35 struct Info {
36     int v, k, p;
37     inline bool operator<(const Info &o) const {return v > o.v;}
38 };
39 int n, m, a[100010];
40 priority_queue < Info > q;
41 int main() {
42     read(n); read(m); m *= 2; root = newNode();
43     for (int i = 1; i <= n; ++i) read(a[i]), insert(a[i]);
44     for (int i = 1; i <= n; ++i) q.push((Info){query(a[i], 2), 2, i});
45     while (m--) {
46         Info h = q.top(); q.pop();
47         if (m & 1) printf("%d ", h.v);
48         if (h.k == n) continue;
49         q.push((Info){query(a[h.p], h.k + 1), h.k + 1, h.p});
50     }
51     return 0;
52 }
View Code

堆+01trie。

在trie树上的每个节点处记录一个v,表示经过的次数,这样就可以资磁查询第k大的操作了。

用一个堆来维护当前最小的异或值。要记录这个值是从哪里来的,第几大。

因为$x_i \; xor \; x_j$ 和 $x_j  \;xor\; x_i$ 是一种情况,所以要循环2k次,并在奇数次输出。

然后再找到a_i异或后的第k+1大值,放入堆中。 

 

BZOJ - 4103

 1 #include<queue>
 2 #include<cstdio>
 3 #include<iostream>
 4 using namespace std;
 5 inline char nc() {
 6     static char b[1<<14],*s=b,*t=b;
 7     return s==t&&(t=(s=b)+fread(b,1,1<<14,stdin),s==t)?-1:*s++;
 8 }
 9 inline void read(int &x) {
10     char b = nc(); x = 0;
11     for (; !isdigit(b); b = nc());
12     for (; isdigit(b); b = nc()) x = x * 10 + b - '0';
13 }
14 struct Node {
15     Node *ch[2]; int sz;    
16 } Tnull, *null = &Tnull, *rt[300010], *L[1005], *R[1005];
17 Node mem[20000000], *C = mem;
18 inline Node* newNode() {
19     C->ch[0] = C->ch[1] = null; C->sz = 0; return C++;    
20 }
21 int n, m, a[1005], q;
22 void insert(int x, Node *u, Node *v) {
23     for (int c, i = 30; ~i; --i) {
24         c = (x >> i) & 1;
25         if (u->ch[c] == null) u->ch[c] = newNode();
26         u->ch[!c] = v->ch[!c];
27         u = u->ch[c]; v = v->ch[c]; u->sz = v->sz + 1;
28     }
29 }
30 int query(Node *ll, Node *rr, int l, int r, int k) {
31     int res = 0;
32     for (int i = l; i <= r; ++i) L[i] = ll, R[i] = rr;
33     for (int c, sz, d = 30; ~d; --d) {
34         sz = 0;
35         for (int i = l; i <= r; ++i) {
36             c = (a[i] >> d) & 1;
37             sz += R[i]->ch[c]->sz - L[i]->ch[c]->sz;
38         }
39         if (k <= sz) {
40             for (int i = l; i <= r; ++i) {
41                 c = (a[i] >> d) & 1;
42                 L[i] = L[i]->ch[c]; R[i] = R[i]->ch[c];
43             }
44         } else {
45             k -= sz; res |= 1 << d; 
46             for (int i = l; i <= r; ++i) {
47                 c = !((a[i] >> d) & 1);
48                 L[i] = L[i]->ch[c]; R[i] = R[i]->ch[c];
49             }
50         }
51     } return res;
52 }
53 int main() {
54     read(n); read(m); null->ch[0] = null->ch[1] = null;
55     for (int i = 0; i <= m; ++i) rt[i] = newNode();
56     for (int i = 1; i <= n; ++i) read(a[i]);
57     for (int t, i = 1; i <= m; ++i) read(t), insert(t, rt[i], rt[i-1]);
58     read(q);
59     for (int i = 0, u, d, l, r, k; i < q; ++i) {
60         read(u); read(d); read(l); read(r); read(k);
61         printf("%d\n", query(rt[l-1], rt[r], u, d, (r - l + 1) * (d - u + 1) - k + 1));
62     }
63     return 0;
64 }
View Code

注意到n很小而m很大,所以我们在m上建立可持久化trie树,n直接暴力就行。

查询第k大也很好搞,咱只要看看使异或值小的个数就行,看看与当前二进制位相同的儿子的size就行。

转载于:https://www.cnblogs.com/p0ny/p/8111567.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值