CF1438D Powerful Ksenia

CF1438D Powerful Ksenia

题目来源:Codeforces, Codeforces Round #682 (Div. 2),CF1438D Powerful Ksenia

题目大意

题目链接

给出 \(n\) 个正整数 \(a_1,a_2,\dots,a_n\)

在一次操作中,你可以:

  • 选择三个不同的下标 \(i\), \(j\), \(k\)
  • \(a_i\), \(a_j\), \(a_k\) 三个值,全部置为 \((a_i\operatorname{xor} a_j\operatorname{xor} a_k)\)

请通过不超过 \(n\) 次操作,使得序列里所有数都相等。注意:不需要最小化操作数量。

数据范围:\(1\leq n\leq 10^5\)\(1\leq a_i\leq 10^9\)

本题题解

首先,当 \(n\) 为奇数时,不论 \(a\) 中的数是什么,我们一定可以构造出一组解。具体方法如下:

考虑,如果序列里,除某个数 \(a_p\) 外,其他(剩下偶数个数)数能两两配对,使得每对数相等。设配出的 \(\frac{n-1}{2}\) 对下标分别为 \((u_1,v_1),(u_2,v_2),\dots,(u_{\frac{n-1}{2}},v_{\frac{n-1}{2}})\)。则我们接下来只需要依次操作:

  • \((p,u_1,v_1)\)
  • \((p,u_2,v_2)\)
  • \(\dots\)
  • \((p,u_{\frac{n-1}{2}},v_{\frac{n-1}{2}})\)

就能使得序列里所有数都等于 \(a_p\)。这是因为 \(a_{u_i}=a_{v_i}\),所以每次三个数异或起来时,\(a_{u_i},a_{v_i}\) 就会自己抵消掉,剩下 \(a_p\)。即:\(\forall i:(a_{u_i}\operatorname{xor}a_{v_i}\operatorname{xor}a_p)=a_p\)。这部分一共需要 \(\frac{n-1}{2}\) 次操作。

思考如何达到这种 \(\frac{n-1}{2}\) 对数两两相等的局面。可以依次对所有 \(i=1,3,5,\dots ,n-2\),做操作 \((i,i+1,i+2)\)。这样,就能使所有 \((i,i+1)\) 相等(\(i=1,3,5,\dots,n-2\))。最后剩下 \(p=n\)。然后进行上述操作即可。

总共需要 \(\frac{n-1}{2}+\frac{n-1}{2} = n-1\) 次操作。


\(n\) 是偶数时,首先要观察到,一次操作不会改变序列里所有数的异或和。而最终序列中要求所有数都相等,异或和一定是 \(0\),所以原序列的异或和也一定要是 \(0\)

那么首先判断,如果原序列异或和不为 \(0\),则直接输出 \(\text{NO}\)

否则我们随便拿出一个数,不妨拿出 \(a_n\)。剩下奇数个数 \(a_1,a_2,\dots,a_{n-1}\),通过上述的构造,一定能变成所有数都相等的情况,设这个相等的值为 \(x\)。那么这 \(n-1\) 个数的异或和也为 \(x\)。因为序列里所有数异或和为 \(0\),所以一定有 \(x=a_n\)。于是我们根本不需要考虑 \(a_n\),直接对前 \(n-1\) 个数用奇数的方法构造即可。


时间复杂度 \(O(n)\)

总结

\(n\) 是奇数的情况,是比较简单的构造。自己手玩一会应该就能看出来。

\(n\) 是偶数时,用到一个技巧:观察每次操作不变的量(例如本题中是异或和。其他题目有时是奇偶性等等)。那么,开始局面的这个量,必须和目标局面相等。也就是说,我们找到一个必要条件。然后思考这个必要条件是否充分。如果充分,我们也就构造出了操作的方法。

参考代码

// problem: CF1438D
#include <bits/stdc++.h>
using namespace std;

#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())

typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

template<typename T> inline void ckmax(T& x, T y) { x = (y > x ? y : x); }
template<typename T> inline void ckmin(T& x, T y) { x = (y < x ? y : x); }

const int MAXN = 1e5;
int n, a[MAXN + 5];
struct Oper_t {
	int i, j, k;
	Oper_t() {}
	Oper_t(int _i, int _j, int _k) { i = _i, j = _j, k = _k; }
};
void solve_odd() {
	cout << "YES" << endl;
	vector<Oper_t> ans;
	for (int i = 1; i <= n - 2; i += 2) {
		ans.pb(Oper_t(i, i + 1, i + 2));
	}
	for (int i = 1; i <= n - 2; i += 2) {
		ans.pb(Oper_t(i, i + 1, n));
	}
	cout << SZ(ans) << endl;
	for (int i = 0; i < SZ(ans); ++i) {
		cout << ans[i].i << " " << ans[i].j << " " << ans[i].k << endl;
	}
}
void solve_even() {
	int xorsum = 0;
	for (int i = 1; i <= n; ++i) xorsum ^= a[i];
	if (xorsum) {
		cout << "NO" << endl;
		return;
	}
	--n;
	solve_odd();
}
int main() {
	cin >> n;
	for (int i = 1; i <= n; ++i) cin >> a[i];
	if (n % 2 == 0) {
		solve_even();
	} else {
		solve_odd();
	}
	return 0;
}
posted @ 2020-11-19 19:17  duyiblue  阅读(489)  评论(1编辑  收藏  举报