csp2025-j

T3
P14359 [CSP-J 2025] 异或和 / xor

看题目范围,感觉是dp。所以先想状态定义

常规套路

int f[N];//f[i]表示长度为i的序列所能选出的区间数量的最大值;

状态转移思路:

f[i]=f[i-1];// 不选第i个数
f[i]=max(f[j]+1);// 选第i个数, j<i 且 f[j+1] ^ f[j+2]...^f[i]==k
f[0]=0;//边界条件

60分解法

#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
int n, k, a[N];
int f[N];//f[i]表示长度为i的序列所能选出的区间数量的最大值;
//f[i]=f[i-1];// 不选第i个数
//f[i]=max(f[j]+1);// 选第i个数, j<i且 f[j+1]^f[j+2]...^f[i]==k
//f[0]=0;//边界条件
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> k;
    for (int i = 1; i <= n; i++)    
    {
        cin >> a[i];
    }

    for (int i = 1; i <= n; i++)
    {
        f[i] = f[i - 1];
        int xor_sum = 0;
        for (int j = i; j >= 1; j--)
        {
            xor_sum ^= a[j];
            if (xor_sum == k)
            {
                f[i] = max(f[i], f[j - 1] + 1);
            }
        }
    }
    cout << f[n];
    return 0;
}

观察到f[j]要尽量靠近f[i]肯定更优, 因为这样就留给前面数据更多机会凑出k,所以一个简单的优化,又可以多20分

            if (xor_sum == k)
            {
                f[i] = max(f[i], f[j - 1] + 1);
                break;//提早退出
            }

但这样还不够。 这就要转化思维
我们第二个循环目的是找出最大的f[j],使得后面一串数异或为k
如果用前缀和思路,记 prexor[i] 为异或前缀和, 那么就是找到prexor[i]^prexor[j]=k
也就是prexor[j]=prexor[i]^k
那么我们就是要找到最大的f[j],并且前缀和prexor[j]=prexor[i]^k
我们可以用一个数组存储当前缀和为特定值的时候,最大的f[j],这样就可以O(1)时间得到答案。

#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
int n, k, a[N];
int f[N];//f[i]表示长度为i的序列所能选出的区间数量的最大值;
//f[i]=f[i-1];// 不选第i个数
//f[i]=max(f[j]+1);// 选第i个数, j<i且 f[j+1]^f[j+2]...^f[i]==k
//f[0]=0;//边界条件

int prexor[N];//prexor[i]表示前i个数的异或和
int best[1<<20];//best[x]表示前i个数中,prexor值为x的最大f值
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> k;
    for (int i = 1; i <= n; i++)    
    {
        cin >> a[i];
        prexor[i] = prexor[i - 1] ^ a[i];
    }

    
    
    memset(best, -1, sizeof best);
    best[0] = 0;//边界条件
    for (int i = 1; i <= n; i++)
    {
        f[i] = f[i - 1];//不选第i个数
        int need = prexor[i] ^ k;//需要找的prexor值
        if (best[need] != -1)//找到了
        {
            f[i] = max(f[i], best[need] + 1);//选第i个数
        }
        best[prexor[i]] = max(best[prexor[i]], f[i]);//更新best数组
    }
    cout << f[n];
    return 0;
}

posted @ 2025-11-03 10:53  katago  阅读(6)  评论(0)    收藏  举报