hdoj 5845 Best Division 【字典树 + dp】

hdoj 5845 Best Division

题意:给定 n 个正整数,要求分成K段连续区间,使得所有区间的最大异或值 <=X <script type="math/tex" id="MathJax-Element-489"><= X</script> 且 区间长度 <=L <script type="math/tex" id="MathJax-Element-490"><= L</script>。现在给你 X L,问你最大的 K

dp[i] a[1]a[i] 的最优解
dp[i]=max(dp[j])+1,(iL<=j<i && sum[j]sum[i]<=X)
其中 sum[i] 记录前缀异或和

类似求解区间最大(小)异或连续子序列的方法,我们在字典树上依次插入前缀和。
对条件 iL<=j<i 的维护,每次判断区间长度,多出就删除对应的前缀和。
sum[j]sum[i]<=X) 的维护看上去很难,第一眼以为要在字典树上暴力DFS,其实不然。考虑第 i 个二进制位:
X的二进制为 1 sum[i] 1
保证异或后值为0,之后的二进制位怎样取都是合法的,可以提前跳出(前提我们要知道继续走下去可以得到的最大 dp 值)。
保证异或后值为 1 ,继续走就好了(最多走完每个二进制位)。
......
其他一样的道理,也就是说没必要去跑DFS,只需扫一遍二进制位即可。
这里我们只需要维护一个 Max[u] 表示以 u 节点为root的子树上的最大 dp 值。

如果当前的 dp 值不存在,我直接插入的是 1
比较无语的是:
10 5 5
1 1 1
结果应该是0,但是一直返回1。然后设个标记就神奇返回0,真心醉,估计哪里没写好。

#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<LL, LL> pii;
const int MAXN = 1e5 + 10;
const int INF = 1e7 + 10;
const int MOD = 1e9 + 7;
void add(LL &x, LL y) { x += y; x %= MOD; }
int f[31];
int X, l, A[MAXN], sum[MAXN], dp;
bool vis[MAXN];
int N;
int Next[MAXN * 30][2], root, L, word[MAXN * 30], Max[MAXN * 30], father[MAXN];
int newnode() {
    Next[L][0] = Next[L][1] = -1;
    father[L] = Max[L] = -1; word[L++] = 0;
    return L - 1;
}
void init() { L = 0; root = newnode(); }
void PushUp(int u) {
    while(u != root) {
        u = father[u];
        Max[u] = max(Max[Next[u][0]], Max[Next[u][1]]);
    }
}
void Insert(int val, int ans) {
    int u = root, v;
    for(int i = 30; i >= 0; i--) {
        v = val & f[i] ? 1 : 0;
        if(Next[u][v] == -1) {
            Next[u][v] = newnode();
        }
        father[Next[u][v]] = u;
        u = Next[u][v]; word[u]++;
        Max[u] = max(Max[u], ans);
    }
    PushUp(u);
}
void Delete(int val) {
    int u = root, v;
    for(int i = 30; i >= 0; i--) {
        v = val & f[i] ? 1 : 0;
        u = Next[u][v];
        word[u]--;
        if(word[u] == 0) {
            Max[u] = -1;
        }
    }
    PushUp(u);
}
int Query(int val) {
    int u = root, v;
    int ans = -1;
    for(int i = 30; i >= 0; i--) {
        v = val & f[i] ? 1 : 0;
        // X
        // 0 -> Next[u][v]:0
        // 1 -> Next[u][v]:0, Next[u][v ^ 1]:1
        if((X & f[i]) && Next[u][v] != -1) {
            ans = max(ans, Max[Next[u][v]]);
        }
        if((X & f[i])) {
            u = Next[u][v ^ 1];
        }
        else {
            u = Next[u][v];
        }
        if(u == -1) {
            break;
        }
    }
    if(u != -1) ans = max(ans, Max[u]);
    return ans;
}
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, &X, &l);
        int P, Q; scanf("%d%d%d", &A[1], &P, &Q);
        sum[1] = A[1];
        for(int i = 2; i <= N; i++) {
            A[i] = (int)(1LL * A[i - 1] * P % 268435456 + Q) % 268435456;
            sum[i] = sum[i - 1] ^ A[i];
        }
        sum[0] = 0; init(); CLR(vis, false);
        Insert(sum[0], 0); int s = 0;
        for(int i = 1; i <= N; i++) {
            if(i - l > s) {
                if(vis[s]) {
                    Delete(sum[s]);
                }
                s++;
            }
            dp = Query(sum[i]) + 1;
            if(dp == 0) continue;
            vis[i] = true; Insert(sum[i], dp);
        }
        printf("%d\n", dp);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值