2023牛客暑期多校训练营7 CI「位运算」「根号分治+容斥」

C-Beautiful Sequence_2023牛客暑期多校训练营7 (nowcoder.com)

题意:

给定一个b序列,a序列满足 a i − 1 < = a i ai-1<=ai ai−1<=ai且 a i ⊕ a i + 1 = b i ai\oplus ai+1=bi ai⊕ai+1=bi,求字典序第k大的满足条件的a序列,若不存在则输出-1。

思路:

我们对b数组求一个前缀和可以发现:

p r e 1 = 0 ⊕ b 1 = a 1 ⊕ a 2 pre1=0\oplus b1=a1\oplus a2 pre1=0⊕b1=a1⊕a2

p r e 2 = p r e 1 ⊕ b 2 = a 1 ⊕ a 2 ⊕ a 2 ⊕ a 3 = a 1 ⊕ a 3 pre2=pre1\oplus b2=a1\oplus a2\oplus a2\oplus a3=a1\oplus a3 pre2=pre1⊕b2=a1⊕a2⊕a2⊕a3=a1⊕a3

......

依次类推,可以发现 p r e i = a 1 ⊕ a i + 1 prei=a1\oplus ai+1 prei=a1⊕ai+1,对于每一组 p r e i prei prei和 p r e i + 1 prei+1 prei+1,考虑二进制从最高位开始查询,当某一位不同的时候一定是因为 a i + 1 ai+1 ai+1的这位为1而 a i ai ai的这位为0,于是我们可以确定 a 1 a1 a1的某一位,遍历完成后会发现可能有一部分位置(sum)的值并未确定,所以所有可能的a序列就有 1 < < s u m 1<<sum 1<<sum个,而字典序第k大的就是 a 1 a1 a1的取值为第k大的,确定 a 1 a1 a1后即可通过b数组求得答案。

AC代码:
cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define eb emplace_back
#define m_p make_pair
const int mod = 998244353;
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define fi first
#define se second
const int inf = 0x3f3f3f3f;
const int N = 1e6 + 50;
//__builtin_ctzll(x);后导0的个数
//__builtin_popcount计算二进制中1的个数
ll a[N],b[N],pre[N];
int numa[33];

void work() {
    int n,k;cin>>n>>k;
    pre[0]=0;a[1]=0;
    for(int i=1;i<n;++i){
        cin>>b[i];
        pre[i]=pre[i-1]^b[i];
    }
    for(int i=0;i<=30;++i)numa[i]=-1;

    for(int i=n-1;i>=1;--i){
        for(int j=30;j>=0;--j){
            if(((pre[i]>>j)&1)!=((pre[i-1]>>j)&1)){
                numa[j]=((pre[i-1]>>j)&1);break;
            }
        }
    }

    ll num=0;
    for(int i=0;i<30;++i){
        if(numa[i]==-1)num++;
    }
    if((1<<num)<k){
        cout<<"-1\n";return;
    }
    k--;

    for(int i=30;i>=0;--i){
        if(numa[i]==-1){
            if((k>>num)&1) numa[i]=1;
            else numa[i]=0;
            num--;
        }
    }

    ll x=1;
    for(int i=0;i<=30;++i){
        if(numa[i]==1)a[1]+=x;
        x*=2;
    }
    for(int i=2;i<=n;++i){
        a[i]=a[i-1]^b[i-1];
        if(a[i]<a[i-1]){
            cout<<"-1\n";return;
        }
    }
    for(int i=1;i<=n;++i){
        cout<<a[i]<<" \n"[i==n];
    }
}

signed main() {
    io;
    int t=1;
    cin >> t;
    while (t--) {
        work();
    }
    return 0;
}

I-We Love Strings_2023牛客暑期多校训练营7 (nowcoder.com)

题意:

给定n个01?字串,其中?可以是0也可以是1,询问有多少个01字串符合至少一个给定的正则串。

思路:

根号分治

在字串长小于20( s q r t ( 400 ) sqrt(400) sqrt(400))时dfs暴力找,总复杂度不会超过 2 21 2^{21} 221。

大于20时,容斥:奇加偶减

AC代码:
cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define eb emplace_back
#define m_p make_pair
const int mod = 998244353;
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define fi first
#define se second
const int inf = 0x3f3f3f3f;
const ll N = 405;
//__builtin_ctzll(x);后导0的个数
//__builtin_popcount计算二进制中1的个数
ll ans=0;
vector<string>a[N];
string tmp;
int n;

void small(int now,int len){
    if (now==len+1){
        for(auto x:a[len]){
            bool ok=1;
            for(int j=0;j<len;j++) {
                if(x[j]=='?') continue;
                if(x[j]!=tmp[j]){
                    ok=0;
                    break;
                }
            }
            if (ok) {
                ans++;
                break;
            }
        }
        return ;
    }
    tmp+='0';
    small(now+1,len);
    tmp.pop_back();

    tmp+='1';
    small(now+1,len);
    tmp.pop_back();
}

void big(int len,int size){
    ll m=1<<size;//正则串随意组合的情况数
    for(int i=1;i<m;++i){//枚举所有可能
        int cnt=0,ok=1;
        tmp.clear();
        for(int j=0;j<len;++j)tmp+="?";
        for(int j=0;j<size;++j){//枚举每种正则串的组合方式
            if(i>>j&1){
                cnt++;
                for(int t=0;t<len;++t){//枚举每一位
                    char x=a[len][j][t];
                    if(tmp[t]=='?'){
                        tmp[t]=x;
                    }else if(x!='?'&&tmp[t]!=x){
                        ok=0;break;
                    }
                }
                if(!ok)break;
            }
        }
        if(!ok)continue;
        int p=1;
        for(int j=0;j<len;++j){
            if(tmp[j]=='?')p=p*2%mod;//计算有多少个未确定的字符,每个未确定代表两种可能
        }
        if(cnt&1){
            ans=(ans+p)%mod;
        }
        else ans=(ans-p)%mod;
    }
}

void work() {
    cin>>n;
    for(int i=1;i<=n;++i){
        string s;cin>>s;
        a[s.size()].pb(s);
    }
    for(int i=1;i<N;++i){
        if(!a[i].size()){
            continue;
        }if(i<=20){
            tmp.clear();
            small(1,i);
        }else{
            big(i,a[i].size());
        }
    }
    ans%=mod;
    cout<<ans<<'\n';
}

signed main() {
    io;
    int t=1;
    //cin >> t;
    while (t--) {
        work();
    }
    return 0;
}
相关推荐
wanderist.16 分钟前
Lambda表达式在算法竞赛中的应用
java·开发语言·算法
zh路西法1 小时前
【10天速通ROS2-PX4无人机】(四) 关掉GPS和气压计,纯激光定位还能飞吗
c++·无人机·px4·ros2·卡尔曼滤波·fastlio2
稚南城才子,乌衣巷风流1 小时前
支配树(Dominator Tree)详解:概念、算法与应用
算法
稚南城才子,乌衣巷风流1 小时前
动态开点:原理、实现与应用场景
数据结构·算法
yyds_yyd_100862 小时前
1464. 数组中两元素的最大乘积(2026.07.27)
数据结构·c++·算法·leetcode
KaMeidebaby2 小时前
卡梅德生物技术快报 | 核酸适配体文库测序:核酸适配体文库测序的技术原理、实验流程与数据解析
前端·网络·数据库·人工智能·算法
geovindu3 小时前
CSharp: Breadth First Search Algorithm and Depth First Search Algorithm
开发语言·后端·算法·c#·.net·搜索算法
库克克4 小时前
【C++】set 与multiset
开发语言·c++
Mortalbreeze4 小时前
深入 Linux Socket 编程:端口号、网络字节序与 struct sockaddr 详解
linux·服务器·网络·c++
ekkoalex4 小时前
训练框架以及算法实现的框架
算法