首页 > 其他分享 >B. 异或(xor)

B. 异或(xor)

时间:2024-10-17 14:23:44浏览次数:1  
标签:xor 差分 异或 序列 操作 变成 双点

B. 异或(xor)

题意

给你 \(n\) 个数,你可以执行若干次操作,每次选择一个区间异或上一个数,问最少操作次数使得所有数变成 \(0\),输出最小的操作次数。

\(n \le 17,a_i \le 10^8\)

solution

居然还有异或差分这种思想,是我孤陋寡闻了

因为是区间操作,所以我们考虑在异或差分序列上操作。显然异或是满足差分性质的。

每次操作相当于对两个数异或上 \(x\),如果操作区间是一段后缀就相当于对一个数异或上 \(x\)。

所有数变成 \(0\) 就相当于差分数组变成 \(0\)。

因此题目变成对于一个序列,可以单点或双点异或上一个数,问最小操作次数使所有数变成 \(0\)。

题解转成图论思考,感觉想法过于聪明,或许直接从序列入手本质上也是一样的。

首先如果有两个一样的数,肯定对这两个数进行一次操作变成 \(0\) 是最优的。直接忽略它们。

对于其他数,进行单点异或一定可以把它变成 \(0\)。

如果只进行单点异或操作,可以得到答案上界,是 \(n\)。为了得到更小的答案,我们不得不做一些双点操作。

考虑进行双点操作发生了什么,发现两个数的异或和操作完不变。

然后可能需要敏锐的观察力。对于一堆异或和不为 \(0\) 的数,你无法只使用双点操作把它们都变成 \(0\),最后剩下一个数的时候你必须使用一次单点操作把它变成 \(0\),因此我们干脆把这个数从这一堆里踢出去,所有操作都不带上这个数,答案不会更劣。

因此我们只研究一堆异或和为 \(0\) 的数。假设这一堆有 \(x\) 个数,那么我们可以只进行双点操作把它们全部变成 \(0\)。发现对于极小的一堆异或和为 \(0\) 的数,最优的操作是每次操作 \((a,b)\) 的时候使它们都异或上\(a\),答案下界是 \(x-1\)。

因此我们要划分出尽可能多的极小的异或和为 \(0\) 的子序列,看到 \(n\) 很小,可以状压 DP。

题解给出的子集枚举转移的做法总复杂度是 \(O(3^n)\) 的。参考黄队的实现,可以做到 \(O(2^n n)\)。

对每个子序列枚举加入哪一个数转移。如果当前状态的异或和为 \(0\),又是因为异或的可差分性质,说明当前状态可以分出一个新的异或和为 \(0\) 的子序列。

跑得飞快,比黄队还快。数据可以加强了

code

#include<bits/stdc++.h>
// #define LOCAL
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
using namespace std;
typedef long long ll;
constexpr int N=18;
int n,a[N+2];
int s[1<<N],f[1<<N];
void _max(int &a,int b) { a=max(a,b); }
int main() {
	#ifdef LOCAL
	#else
	freopen("xor.in","r",stdin);
	freopen("xor.out","w",stdout);
	#endif
	sf("%d",&n);
	rep(i,1,n) sf("%d",&a[i]);
	rep(i,1,n-1) a[i]^=a[i+1];
	rep(i,1,(1<<n)-1) rep(j,0,n-1) if((i>>j)&1) s[i]^=a[j+1];
	rep(i,1,(1<<n)-1) {
		if(!s[i]) f[i]++;
		rep(j,0,n-1) if(!((i>>j)&1)) _max(f[i|(1<<j)],f[i]); 
	}
	pf("%d\n",n-f[(1<<n)-1]);
}

标签:xor,差分,异或,序列,操作,变成,双点
From: https://www.cnblogs.com/liyixin0514/p/18472145

相关文章

  • 【广西省赛#7】G.Grand XOR Counting Problem Challenge
    Description给一个数组\({a_i},i=1,\cdots,n\),对\(j=0,1,\cdots,m-1\),计算其中有多少个大小为\(k\)的子序列满足其异或和为\(j\)。\(n\leq10^5\)$m\leq65536$Solution首先答案是\[[y^k]\prod_{i=1}^n(1+x^{a_i}y)\]这里对\(y\)做的是多项式乘法,对\(x\)......
  • 【题解】P3917 异或序列
    传送门也算是一个有关于异或的小trick吧,简单记录一下。可以维护原序列的前缀异或和\(sum\),于是原题答案贡献变为\(\sum\limits_{i=1}^n\sum\limits_{j=i}^nsum_j\oplussum_{i-1}\)。变形一下为\(\sum\limits_{i=0}^{n-1}\sum\limits_{j=1}^{i+1}sum_i\oplussum_{j}......
  • 洛谷题单指南-字符串-P5283 [十二省联考 2019] 异或粽子
    原题链接:https://www.luogu.com.cn/problem/P5283题意解读:n个整数,每次从从取l~r的数进行异或得到美味值,一共取k次,并计算这k个美味值之和的最大值。解题思路:1、如何O(1)的计算l~r数的异或,得到美味值可以借助前缀和思想,a[i]为第i个数,s[i]表示a[1]~a[i]每个数的异或值,要计算l~r的......
  • [AGC061E] Increment or XOR
    题目中涉及到了加法和异或,一个是进位加法,一个是不进位加法,显得很不可做。但是我们注意到加法只加\(1\),如果产生进位了,那会将末尾的所有\(1\)推平成\(0\),而如果没有进位,则后面的位不会受到加法影响。这启发我们挖掘这道题的过程。我们发现这个过程形似可以从低位推到高位,并且......
  • 3158. 求出出现两次数字的 XOR 值
    给你一个数组nums,数组中的数字要么出现一次,要么出现两次。请你返回数组中所有出现两次数字的按位XOR值,如果没有数字出现过两次,返回0。示例1:输入:nums=[1,2,1,3]输出:1解释:nums中唯一出现过两次的数字是1。示例2:输入:nums=[1,2,3]输出:0解释:nums中没有数......
  • CF959F Mahmoud and Ehab and yet another xor task 题解
    题目传送门前置知识线性基解法将操作离线下来,并按\(\{l\}\)升序排序,接着顺序插入线性基并处理询问。对于未成功插入线性基的元素\(k\)一定能被线性基内选出若干元素得到。故在\(x\)能被表出的情况下,设\(1\siml\)中成功插入线性基的元素个数为\(siz\),对于剩下\(......
  • AT_abc283_g [ABC283G] Partial Xor Enumeration 题解
    题目传送门前置知识线性基解法考虑线性基。因为有可空子序列也参与运算,所以第\(1\)小的数是\(0\);但线性基中是不允许有异或和为\(0\)的存在,故线性空间内第\(k-1\)小的数就是所求的第\(k\)小的数。令每一个二进制位仅在它这一位的基底上出现,其他位上的基底直接异或......
  • Codeforces Round 316 (Div. 2) D题 Tree Requests(二分,dfs,在线,前缀异或)
    题目链接CodeforcesRound316(Div.2)D题TreeRequests思路将262626个字母全部当作一个二进制数。将每个深度的结点按照dfs序放到一个vector里,同时记录每个vector......
  • 题解:P11008 『STA - R7』异或生成序列
    Solution序列\(p\)是\(1\)~\(n\)的排列,因此考虑搜索回溯。由\(\sumn\le2\times10^6\)得知\(O(n^2)\)会炸,深感遗憾但仍考虑剪枝。坚信深搜过百万的蒟蒻。。。原\(b\)序列为长度\(n-1\)的序列:{\(b_1,b_2,b_3\cdotsb_n-1\)}将其前面插入一个元素\(......
  • [ABC150F] Xor Shift
    题意给定两个序列\(a,b\),求将\(b\)循环移位\(k\)位,再给所有\(b_i\oplusx\),求所有满足条件的\((k,x)\)。\(n\le2\times10^5\)。Sol对于区间异或,容易想到差分。考虑对\(a\)和\(b\)分别差分,注意到差分后\(x\)直接消失了!也就是:\(a_0\oplusa_1=b_{(......