CodeForce 1438 D. Powerful Ksenia 【位运算、思维】

题目链接

 三个数进行异或,三个数和的奇偶性不变。或者说三个数,任意一位上 1 1 1 的个数的奇偶性不变。也就是说,每次操作后, n n n 个数每一位上的 1 1 1 的个数的数量要么不变 (±0),要么 ± 2 ±2 ±2

 如果 n n n 是奇数,对于第 k k k 位,如果这一位上 1 1 1 的数量是奇数,那剩下 0 0 0 的数量就是偶数,我们最终只能通过增加偶数个 1 1 1 n n n 个数的第 k k k 位都置为 1 1 1 1 1 1 的数量为偶数也同理,就只能把 n n n 个数的第 k k k 位都置为 0 0 0。也就是说, n n n 为奇数一定有解,而且最终这相同的 n n n 个数等于最初这 n n n 个数的异或和。

 偶数也是一样分析,如果有某一位上 1 1 1 的个数为奇数就无解,这种情况下所有数的异或和也肯定不为 0 0 0。不然就有解,不过解不唯一,因为我可以去掉偶数个 1 1 1,也可以补齐偶数个 1 1 1

 官方题解就很妙:首先对形如 a   b   b a\ b\ b a b b 的数进行异或,得到的是 a a a。然后如果是奇数,就是 { 1 , 2 , 3 } , { 3 , 4 , 5 } , . . . , { k − 2 , k − 1 , k } { k , k + 1 , k + 2 } \{ 1,2,3 \},\{ 3,4,5 \},...,\{ k-2,k-1,k \}\{ k,k+1,k+2 \} {1,2,3},{3,4,5},...,{k2,k1,k}{k,k+1,k+2} 这样进行操作,一共 n 3 \frac{n}{3} 3n 次…哦好像是 n − 1 2 \frac{n-1}{2} 2n1 次,由于是奇数所以最后一次操作一定是 { n − 2 , n − 1 , n } \{ n-2,n-1,n \} {n2,n1,n}。然后,由于每次都是“叠”着上一次操作的第三个数,这样可以把前面的数的异或和“传递”给下一组三元组,这样最后一次操作后得到的三个数,就是 n n n 个数的异或和。接下来,数组大概变成这样子: { a , a , b , b , c , c . . . m , m , n , n , n } \{ a,a,b,b,c,c...m,m,n,n,n \} {a,a,b,b,c,c...m,m,n,n,n},就是用 a   b   b a\ b\ b a b b 的形式了。
 如果是偶数,先判断 n n n 个数的异或和是否为 0 0 0,然后对前 n − 1 n-1 n1 个数进行操作,和奇数一样,最后一个数:

To solve for even n and X=0, we can just solve the problem for the first n−1 using the odd approach and the last element will magically be equal to the first n−1. ——(来自官方题解)

struct Triple {
    int x,y,z;
    Triple(int a=0,int b=0,int c=0){ x=a; y=b; z=c; }
};

vector<Triple>ans;
int a[200100];

void solve(int n) {
    for(int i=1;i+2<=n;i+=2) {
        ans.push_back( Triple(i,i+1,i+2) );
    }
    for(int i=1;i<n;i+=2) {
        ans.push_back( Triple(i,i+1,n) );
    }
}

int main() {
    int T = 1;
    // scanf("%d",&T);
    while(T--) {
        int n, m;
        scanf("%d",&n);

        for(int i=1;i<=n;i++)scanf("%d",a+i);

        int XOR = 0;
        for(int i=1;i<=n;i++) XOR = XOR ^ a[i];

        if( n&1 ) {
            solve(n);
        }
        else {
            if( XOR != 0 ){ printf("NO\n"); }
            else solve(n-1);
        }

        if( !ans.empty() ) {
            printf("YES\n");
            printf("%d\n",ans.size());
            for(auto [x,y,z] : ans)printf("%d %d %d\n",x,y,z);
            printf("\n");
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值