字典树

poj 3764 The xor-longest Path
题意:给定一棵树,问你最长的异或路径。

思路:想到 LCA 就差不多了,我们记录根到节点i路径的 ans[i]
这样对于 u>v 路径的异或值为 ans[u]ans[v]
相当于给定 n1 个数,求解 max (两两异或的最大值, 单个的最大值)。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstring>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <cstdlib>
#define ll o<<1
#define rr o<<1|1
#define CLR(a, b) memset(a, (b), sizeof(a))
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int MAXN = 1e5 + 10;
const int INF = 1e9 + 10;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int MOD = 1e9 + 7;
void add(LL &x, LL y) { x += y; x %= MOD; }
struct Edge {
    int from, to, val, next;
};
Edge edge[MAXN * 2];
int head[MAXN], edgenum;
void addEdge(int u, int v, int w) {
    Edge E = {u, v, w, head[u]};
    edge[edgenum] = E;
    head[u] = edgenum++;
}
int Next[31 * MAXN][2], word[31 * MAXN], L, root;
int f[40];
int newnode() {
    Next[L][0] = Next[L][1] = -1;
    word[L++] = 0;
    return L - 1;
}
void init() { L = 0; root = newnode(); }
void Insert(int val) {
    int u = root;
    for(int i = 30; i >= 0; i--) {
        int v = (val & f[i]) ? 1 : 0;
        if(Next[u][v] == -1) {
            Next[u][v] = newnode();
        }
        u = Next[u][v];
        word[u]++;
    }
}
int Query(int val) {
    int ans = 0; int u = root;
    for(int i = 30; i >= 0; i--) {
        int v = (val & f[i]) ? 1 : 0;
        if(v == 0) {
            if(Next[u][1] != -1 && word[Next[u][1]]) {
                ans ^= f[i]; u = Next[u][1];
            }
            else {
                u = Next[u][0];
            }
        }
        else {
            if(Next[u][0] != -1 && word[Next[u][0]]) {
                ans ^= f[i]; u = Next[u][0];
            }
            else {
                u = Next[u][1];
            }
        }
    }
    return ans;
}
int val[MAXN];
void DFS(int u, int fa) {
    for(int i = head[u]; i != -1; i = edge[i].next) {
        int v = edge[i].to;
        if(v == fa) continue;
        val[v] = val[u] ^ edge[i].val;
        DFS(v, u);
    }
}
int main()
{
    f[0] = 1;
    for(int i = 1; i <= 30; i++) {
        f[i] = f[i - 1] * 2;
    }
    int n;
    while(scanf("%d", &n) != EOF) {
        for(int i = 1; i <= n; i++) head[i] = -1, val[i] = 0;
        edgenum = 0;
        for(int i = 1; i <= n - 1; i++) {
            int u, v, w; scanf("%d%d%d", &u, &v, &w);
            u++; v++;
            addEdge(u, v, w); addEdge(v, u, w);
        }
        DFS(1, -1); init(); int ans = 0;
        for(int i = 2; i <= n; i++) {
            Insert(val[i]);
            ans = max(ans, val[i]);
        }
        for(int i = 2; i <= n; i++) {
            ans = max(Query(val[i]), ans);
        }
        printf("%d\n", ans);
    }
    return 0;
}

lightoj 1269 - Consecutive Sum

题意:给你连续子序列的最大异或值和最小异或值。
维护前缀异或和即可。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstring>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <cstdlib>
#define ll o<<1
#define rr o<<1|1
#define CLR(a, b) memset(a, (b), sizeof(a))
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int MAXN = 5e4 + 10;
const int INF = 1e9 + 10;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int MOD = 1e9 + 7;
void add(LL &x, LL y) { x += y; x %= MOD; }
struct Edge {
    int from, to, val, next;
};
Edge edge[MAXN * 2];
int head[MAXN], edgenum;
void addEdge(int u, int v, int w) {
    Edge E = {u, v, w, head[u]};
    edge[edgenum] = E;
    head[u] = edgenum++;
}
int Next[31 * MAXN][2], word[31 * MAXN], L, root;
int f[40];
int newnode() {
    Next[L][0] = Next[L][1] = -1;
    word[L++] = 0;
    return L - 1;
}
void init() { L = 0; root = newnode(); }
void Insert(int val) {
    int u = root;
    for(int i = 30; i >= 0; i--) {
        int v = (val & f[i]) ? 1 : 0;
        if(Next[u][v] == -1) {
            Next[u][v] = newnode();
        }
        u = Next[u][v];
        word[u]++;
    }
}
int Query_Max(int val) {
    int u = root;
    for(int i = 30; i >= 0; i--) {
        int v = (val & f[i]) ? 1 : 0;
        if(v == 0) {
            if(Next[u][1] != -1 && word[Next[u][1]]) {
                val ^= f[i]; u = Next[u][1];
            }
            else {
                u = Next[u][0];
            }
        }
        else {
            if(Next[u][0] != -1 && word[Next[u][0]]) {
                u = Next[u][0];
            }
            else {
                val ^= f[i]; u = Next[u][1];
            }
        }
    }
    return val;
}
int Query_Min(int val) {
    int u = root;
    for(int i = 30; i >= 0; i--) {
        int v = (val & f[i]) ? 1 : 0;
        if(v == 1) {
            if(Next[u][1] != -1 && word[Next[u][1]]) {
                val ^= f[i]; u = Next[u][1];
            }
            else {
                u = Next[u][0];
            }
        }
        else {
            if(Next[u][0] != -1 && word[Next[u][0]]) {
                u = Next[u][0];
            }
            else {
                val ^= f[i]; u = Next[u][1];
            }
        }
    }
    return val;
}
int main()
{
    f[0] = 1;
    for(int i = 1; i <= 30; i++) {
        f[i] = f[i - 1] * 2;
    }
    int t, kcase = 1; scanf("%d", &t);
    while(t--) {
        int n; scanf("%d", &n);
        init(); int ans1 = 0, ans2 = (int)((1LL << 31) - 1);
        int sum = 0;
        for(int i = 1; i <= n; i++) {
            int v; scanf("%d", &v); sum ^= v;
            if(i > 1) {
                ans1 = max(Query_Max(sum), ans1);
                ans2 = min(Query_Min(sum), ans2);
            }
            Insert(sum);
            ans1 = max(sum, ans1);
            ans2 = min(sum, ans2);
        }
        printf("Case %d: %d %d\n", kcase++, ans1, ans2);
    }
    return 0;
}

hdoj 5715 XOR 游戏
有毒。。。
字典树节点 Next[u][0]=1 一直WA,找了一小时bug,发现改成 Next[u][0]=0 莫名AC?

思路:考虑二分答案,这样就变成了判定性问题。
对于当前的答案 o ,我们只要证明n个数分 m 段后所有段最小值大于o即可。
即有 dp[i][j] i 个数分j段对于答案 o 是否合法。
dp[i][j]=(sum[k]sum[i]>=o)(iL<=k<i&& dp[k][j1]==true)

每次在字典树上插入合法的 dp[k][j1] ,然后就是判断在所有合法的 dp[k][j1] 里面是否存在一个 k 使得sum[k]sum[i]>=o iL<=k<i
那样就是求解异或 sum[i] 后的最大值了。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstring>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <cstdlib>
#define ll o<<1
#define rr o<<1|1
#define CLR(a, b) memset(a, (b), sizeof(a))
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int MAXN = 1e4 + 10;
const int INF = 1e9 + 10;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int MOD = 1e9 + 7;
void add(LL &x, LL y) { x += y; x %= MOD; }
int f[31];
struct Trie {
    int Next[31 * MAXN][2], word[31 * MAXN], L, root;
    int newnode() {
        Next[L][0] = Next[L][1] = 0; word[L++] = 0;
        return L - 1;
    }
    void init() { L = 0; root = newnode(); }
    void Insert(int val) {
        int u = root;
        for(int i = 30; i >= 0; i--) {
            int v = (val & f[i]) ? 1 : 0;
            if(Next[u][v] == 0) {
                Next[u][v] = newnode();
            }
            u = Next[u][v];
            word[u]++;
        }
    }
    void Delete(int val) {
        int u = root;
        for(int i = 30; i >= 0; i--) {
            int v = (val & f[i]) ? 1 : 0;
            u = Next[u][v];
            word[u]--;
        }
    }
    int Query(int val) {
        int u = root, ans = 0;
        for(int i = 30; i >= 0; i--) {
            int v = (val & f[i]) ? 1 : 0;
            if(Next[u][v ^ 1] != 0 && word[Next[u][v ^ 1]]) {
                ans ^= f[i]; u = Next[u][v ^ 1];
            }
            else {
                u = Next[u][v];
            }
        }
        return ans;
    }
}; Trie trie[11];
bool dp[MAXN][11];
int sum[MAXN];
int N, M, L;
bool judge(int o) {
    for(int i = 0; i <= M; i++) {
        trie[i].init();
    }
    for(int i = 0; i <= N; i++) {
        for(int j = 0; j <= M; j++) {
            dp[i][j] = false;
        }
    }
    trie[0].Insert(sum[0]); dp[0][0] = true;
    for(int i = 1; i <= N; i++) {
        for(int j = M; j >= 1; j--) {
            if(i - L > 0) {
                if(dp[i - L - 1][j - 1]) {
                    trie[j - 1].Delete(sum[i - L - 1]);
                }
            }
            if(trie[j - 1].Query(sum[i]) >= o) {
                dp[i][j] = true;
                trie[j].Insert(sum[i]);
            }
        }
    }
    return dp[N][M];
}
int main()
{
    f[0] = 1;
    for(int i = 1; i <= 30; i++) {
        f[i] = f[i - 1] * 2;
    }
    int t, kcase = 1; scanf("%d", &t);
    while(t--) {
        scanf("%d%d%d", &N, &M, &L);
        sum[0] = 0;
        for(int i = 1; i <= N; i++) {
            int v; scanf("%d", &v);
            sum[i] = sum[i - 1] ^ v;
        }
        int l = 0, r = (1 << 30) - 1;
        int ans = 0;
        while(r >= l) {
            int mid = l + r >> 1;
            if(judge(mid)) {
                ans = mid;
                l = mid + 1;
            }
            else {
                r = mid - 1;
            }
        }
        printf("Case #%d:\n%d\n", kcase++, ans);
    }
    return 0;
}

几道水题
lightoj 1114 - Easily Readable

lightoj 1129 - Consistency Checker

lightoj 1224 - DNA Prefix
这道题有点意思吧,n个串中选出任意个。问max(串数目 * 公共前缀长度)
就相当于把串扫一遍,统计最小覆盖。中间要加个特判剪枝。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值