题解 | #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';
        }
    }
}
全部评论

相关推荐

行云流水1971:这份实习简历的优化建议: 结构清晰化:拆分 “校园经历”“实习经历” 板块(当前内容混杂),按 “实习→校园→技能” 逻辑排版,求职意向明确为具体岗位(如 “市场 / 运营实习生”)。 经历具象化:现有描述偏流程,需补充 “动作 + 数据”,比如校园活动 “负责宣传” 可加 “运营公众号发布 5 篇推文,阅读量超 2000+,带动 300 + 人参与”;实习内容补充 “协助完成 XX 任务,效率提升 X%”。 岗位匹配度:锚定目标岗位能力,比如申请运营岗,突出 “内容编辑、活动执行” 相关动作;申请市场岗,强化 “资源对接、数据统计” 细节。 信息精简:删减冗余表述(如重复的 “负责”),用短句分点,比如 “策划校园招聘会:联系 10 + 企业,组织 200 + 学生参与,到场率达 85%”。 技能落地:将 “Office、PS” 绑定经历,比如 “用 Excel 整理活动数据,输出 3 份分析表;用 PS 设计 2 张活动海报”,避免技能单独罗列。 优化后需强化 “经历 - 能力 - 岗位需求” 的关联,让实习 / 校园经历的价值更直观。 若需要进一步优化服务,私信
实习,投递多份简历没人回...
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务