CF #814 D2 - Burenka and Traditions (hard version)
DP + map优化转移
题意
给 n (1 <= n <= 1e5) 个元素的数组,每次操作可以选一个区间 和一个非负整数 x,花 的代价让 的元素 a[i] ^= x, 求最小代价使数组元素全部为 0
思路
-
因为操作长度为 len 的区间,产生的效果与代价和操作 次长度为 2 的区间 + len % 2 次长度为 1 的区间一样,所以之后只操作长度为 1 或 2 的区间, 代价均为 1
-
由于代价是区间长度除以 2 向上取整,所以最好操作偶数长度的区间
-
基于 1,2 中贪心的思想,每次尽量操作长度为 2 的区间,因此就先对 操作,为了让它们尽量变为 0,可以选择让它们都异或 ,这样 为 0,
-
同理,对于一段长度为 len 的区间 , 若要把它们都变为 0,按上述方案操作,操作 len - 1 次后 都已变为 0, a[r] = a[l] ^ a[l+1] ^ ... ^ a[r], 此时若 的异或和为 0,则可以省一次操作;否则 还要异或上自身,为了使操作数变小,就是要操作异或和为 0 的区间尽量多(因此也可以转换为找到互不相交的异或和为 0 的区间数量,答案为 n - 该数量,下文并不是这种转移,但也是对的)
-
设 为把前 i 个变为 0 的代价, 初始化 , 表示 单独操作一次
从 1 到 i - 1 枚举 idx,若 的异或和为 0,表示操作 这个区间后可以省一次代价,用 更新答案,复杂度为
-
可以用 map 存下前缀异或和 为 x 的最靠后(用靠后的下标转移肯定比靠前的要优,这也是把转移复杂度从 降到 的关键)的下标 i,即 ;
假设枚举到 r,找到 , 表示 , 即 的异或和为 0,直接用 更新答案
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <map>
using namespace std;
#define endl "\n"
typedef long long ll;
typedef pair<int, int> PII;
const int N = 1e5 + 10;
int a[N];
int s[N], f[N];
map<int, int> mp;
int n;
void init()
{
mp.clear();
mp[0] = 0;
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T;
cin >> T;
while(T--)
{
cin >> n;
init();
for (int i = 1; i <= n; i++)
{
cin >> a[i];
s[i] = s[i-1] ^ a[i];
}
for (int i = 1; i <= n; i++)
{
f[i] = f[i-1] + 1;
int t = s[i];
if (mp.count(t))
{
int idx = mp[t];
f[i] = min(f[i], f[idx] + i - idx - 1);
}
mp[s[i]] = i;
}
cout << f[n] << endl;
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
· 零经验选手,Compose 一天开发一款小游戏!