【训练题59:想法 | 构造】Powerful Ksenia | CF1438D

题意

  • Powerful Ksenia | CF1438D
    给你一个长度为 n n n 的序列 A [ n ] A[n] A[n]
    你最多能操作 n n n 次,每次选择三个位置 i , j , k i,j,k i,j,k,然后让这三个位置的值等于 A [ i ] ⊕ A [ j ] ⊕ A [ k ] A[i]\oplus A[j]\oplus A[k] A[i]A[j]A[k]
    问你,能否使序列中所有数字都相同?若能,给出一种方案
  • 3 ≤ n ≤ 1 0 5 3\le n\le 10^5 3n105
    1 ≤ a i ≤ 1 0 9 1\le a_i\le 10^9 1ai109

思路

  • 想法和构造都太差了,准备记录一下这类题
    首先,我们能操作一次,让三个位置的值都相等
    接下来,我们想一下剩下的要怎么取搞
    因为 [ a , a , b ] [a,a,b] [a,a,b] 操作一次会变成 [ b , b , b ] [b,b,b] [b,b,b]
    [ a , b , c ] [a,b,c] [a,b,c] 操作一次会变成 [ d , d , d ] [d,d,d] [d,d,d]
    所以我们这么操作:[a,a,a,b,c,d,e,f,g] → \rightarrow [a,a,h,h,h,d,e,f,g] → \rightarrow [a,a,h,h,i,i,i,f,g] → \rightarrow [a,a,h,h,i,i,j,j,j]
    然后倒着操作:[a,a,h,h,j,j,j,j,j] → \rightarrow [a,a,j,j,j,j,j,j,j] → \rightarrow [j,j,j,j,j,j,j,j,j]
    容易看到,如果区间长度是奇数,我们一定可以使用这样的构造方法来满足要求。但是如果 n n n 是偶数呢?
  • 我们有一个不变性质。考虑三个数 [ a , b , c ] [a,b,c] [a,b,c] ,有 k = a ⊕ b ⊕ c k=a\oplus b\oplus c k=abc
    我们进行操作之后,这三个数就变成了 [ k , k , k ] [k,k,k] [k,k,k]
    然后,我们发现 k ⊕ k ⊕ k = k k\oplus k\oplus k=k kkk=k 仍然是成立的
    也就是说不管我们怎么操作,这 n n n 个数的异或值都是不变的!
    再返回去看原来 n n n 为奇数的情况,那个最后的 j j j 其实就是所有数的异或值
  • 对于 n n n 偶数来说,如果我们最终变成的数是 [k,k,k,k,k,k] ,容易得到:不管 k k k 为多少,所有数的异或值必须为 0 0 0
    换句话说,如果此时数的异或值非零,那么一定无解;否则,一定有解
    有解的话,构造方法其实是和 n n n 为奇数时一样的,只不过我们最后一个数不去操作
    因为根据奇数的操作方法, 我们最后一定会获得 [a,a,a,a,a,a,a,b] ,因为异或和为 0 0 0 ,所以此时一定 a = b a=b a=b

代码

  • 时间复杂度: O ( n ) O(n) O(n)
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
typedef long long ll;
void show(){std::cerr << endl;}template<typename T,typename... Args>void show(T x,Args... args){std::cerr << "[ " << x <<  " ] , ";show(args...);}

const int MAX = 1e5+50;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const double EPS = 1e-5;


ll aa[MAX];
int main()
{
    int n;scanf("%d",&n);
    ll res = 0;
    for(int i = 1;i <= n;++i){
        scanf("%lld",&aa[i]);
        res ^= aa[i];
    }

    if((n & 1) || (res == 0)){
        if(n % 2 == 0)n--;
        printf("YES\n%d\n",(n-1)/2+(n-1)/2-1);		// 其实就是 n-2
        for(int i = 1;i <= n - 2;i += 2){
            printf("%d %d %d\n",i,i+1,i+2);
        }
        for(int i = n - 4;i >= 1;i -= 2){
            printf("%d %d %d\n",i,i+1,i+2);
        }
    }else{
        puts("NO");
    }

    return 0;
}
/**

*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值