题解 | #Beautiful Sequence#

Beautiful Sequence

https://ac.nowcoder.com/acm/contest/57361/C

牛客多校七 C

  • 将每一个数看成是 3030 位二进制整数
  • 只要确定了 a1a_1, 剩下的数就都确定了 , 因为 ai+1biaia_{i+1} \gets b_i \oplus a_i
    • a2b1a1a_2 \gets b_1 \oplus a_1
    • a3a2b2a1b1b2a_3 \gets a_2 \oplus b_2 \gets a_1 \oplus b_1 \oplus b_2
    • a4a1b1b2b3a_4 \gets a_1 \oplus b_1 \oplus b_2 \oplus b_3
  • sumibibi1sum_i \gets b_i \oplus b_{i-1}, 则 aia1sumi1a_i \gets a_1 \oplus sum_{i-1}
  • 要一对儿一对儿的考虑限制,即考虑所有的sumi,sumi+1sum_i, sum_{i+1} (前缀异或和)
  • 观察发现 a1a_1 的每一位上的数的取值情况可以通过分析前缀异或和得到
  • a1a_1 上的每一位的取值情况一共有三种
    • 只能取 11;
    • 只能取 00;
    • 0,10,1 都能取也就是没有限制。
  • 对于每个限制就是找到 sumi,sumi+1sum_i, sum_{i+1},的二进制表示从左往后第一位不相同的,则 a1a_1 的该位置应该与 sumisum_i 保持一致(异或不同才是11) 如此可保证数组 aa 的单调性;
  • 计算字典序第 kk 小的数 要记得 k1k-1 ,再转成二进制填到 a1a_1 可以变化的位数上去。因为字典序第一小的数是 a1a_1 所有可以变化的位置都为 00 的情况;
  • 时间复杂度 O(n)O(n), 常数为 3030;

参考代码:

#include <cstring>
#include <vector>
#include <iostream>
using namespace std;

typedef long long ll;

const int N =1e6+6;

int a1[30]; // 0-29 表示有效示数
int a[N];
int sum[N]; // 前缀异或和
void solve(){
    int n,k; cin >> n >> k;
    sum[0] = 0;
    for(int i =1; i < n; i++){
        int b;
        cin >> b;
        sum[i] = sum[i-1]^b;
    }
    memset(a1,-1,sizeof a1);
    bool has_ans = true;
    // sum[0] = 0  一共是 n-1 个限制 a_i  <= a_{i+1}
    for(int i = 0; i < n-1; i++){
        // 从高位开始枚举
        if(!has_ans)
            break;
        for(int j =29; j >= 0; j--){
            int t1 = (sum[i]>> j) & 1;
            int t2 = (sum[i+1] >> j) & 1;
            if(t1 != t2){
                if(a1[j] == -1){
                    // 第 i+1 位没有限制时 限制第 i+1 位
                    a1[j] = t1;
                }
                else{
                    if(a1[j] != t1){
                        has_ans = false;
                    }
                }
                break;
            }
        }
    }
    if(!has_ans){
        cout << -1 << '\n';
    }
    else{
        k--; int cnt = 0;
        for(int i =29; i >= 0; i--){
            if(a1[i] == -1){
                cnt = cnt*2+1;
            }
        }
        if(k > cnt){
            cout << -1 << '\n';
        }
        else{
            int p = 0;
            while(k){
               int t = k & 1;
                while (a1[p] != -1 && p <= 29){
                    p++;
                }
                a1[p] = t;
                k >>= 1;
            }
            // 计算a_1 的值
            a[1] = 0;
            for(int i = 29; i>=0; i--){
                if(a1[i] == 1){
                    a[1] = a[1]*2+1;
                }
                else a[1] *=2;
            }
            for(int i =2; i <= n; i++){
                a[i] = a[1]^sum[i-1];
                if(a[i] > 1 << 30){
                    cout << -1 << '\n';
                    return;
                }
            }
            for(int i =1; i <= n; i++){
                cout << a[i] << ' ';
            }
            cout << '\n';
        }
    }
}
全部评论

相关推荐

ALEX_BLX:虽然说聊天记录不可信,不过这个趋势确实如此但我觉得也要想到一点就是卷后端的人里真正有“料”的人又有多少,我说的这个料都不是说一定要到大佬那种级别,而是就一个正常的水平。即使是现在也有很多人是跟风转码的,2-3个月速成后端技术栈的人数不胜数,但今时不同往日没可能靠速成进大厂了。这种情况就跟考研一样,你能上考场就已经打败一半的人了
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务