Codeforces Round 1016 (Div. 3) A-F(略讲)G(详解)

A. Ideal Generator

\(判断奇偶即可\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define all(x) (x).begin(),(x).end()
#define rall(x) (x).rbegin(),(x).rend()
#define pb push_back
#define pii pair<int,int>
using namespace std;
const int mod=998244353;
int gcd(int a,int b){return b?gcd(b,a%b):a;};
int qpw(int a,int b){int ans=1;while(b){if(b&1)ans=ans*a%mod;a=a*a%mod,b>>=1;}return ans;}
int inv(int x){return qpw(x,mod-2);}
void solve(){
    int  n;cin>>n;
    if (n&1) {
        cout<<"YES\n";
    }else cout<<"NO\n";
}
signed main(){
    ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
    int _=1;
    cin>>_;
    while(_--)solve();
}
#B. Expensive Number

\(前缀零保存求最小删除个数\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define all(x) (x).begin(),(x).end()
#define rall(x) (x).rbegin(),(x).rend()
#define pb push_back
#define pii pair<int,int>
using namespace std;
const int mod=998244353;
int gcd(int a,int b){return b?gcd(b,a%b):a;};
int qpw(int a,int b){int ans=1;while(b){if(b&1)ans=ans*a%mod;a=a*a%mod,b>>=1;}return ans;}
int inv(int x){return qpw(x,mod-2);}
void solve(){
    string s;cin>>s;
    int cnt=s.size()-1;
    int mi=1e9;
    for (int i=0;i<s.length();i++) {
        if (s[i]=='0')cnt--;
        else mi=min(mi,cnt);
    }
    cout<<mi<<endl;
}
signed main(){
    ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
    int _=1;
    cin>>_;
    while(_--)solve();
}

C. Simple Repetition

\(只有1为特殊情况 其他情况必定存在类似于10101的格式能够为其的第二个因数 分类讨论一下即可\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define all(x) (x).begin(),(x).end()
#define rall(x) (x).rbegin(),(x).rend()
#define pb push_back
#define pii pair<int,int>
using namespace std;
const int mod=998244353;
int gcd(int a,int b){return b?gcd(b,a%b):a;};
int qpw(int a,int b){int ans=1;while(b){if(b&1)ans=ans*a%mod;a=a*a%mod,b>>=1;}return ans;}
int inv(int x){return qpw(x,mod-2);}
void solve(){
    int x,k;cin>>x>>k;
    if (x==1) {
        for (int i=2;i<=k;i++) {
            x=x*10+1;
        }
        k=1;
    }
    if (k>1||x==1) {
        cout<<"NO\n";return;
    }else {
        for (int i=2;i*i<=x;i++) {
            if (x%i==0) {
                cout<<"NO\n";return;
            }
        }
    }
    cout<<"YES\n";
}
signed main(){
    ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
    int _=1;
    cin>>_;
    while(_--)solve();
}

D. Skibidi Table

\(分型板子题 将其每个部分进行讨论\)
\(发现个数和4^n相关\)
\(位置和2^n相关\)
\(根据题目给出的图做即可\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define all(x) (x).begin(),(x).end()
#define rall(x) (x).rbegin(),(x).rend()
#define pb push_back
#define pii pair<int,int>
using namespace std;
const int mod=1e18;
int gcd(int a,int b){return b?gcd(b,a%b):a;};
int qpw(int a,int b){int ans=1;while(b){if(b&1)ans=ans*a%mod;a=a*a%mod,b>>=1;}return ans;}
int inv(int x){return qpw(x,mod-2);}
int pw[100];
void solve(){
    function<void(int,int,int,int)>dfs1=[&](int now,int x,int y,int n) {
        if (n==-1) {
            cout<<x<<" "<<y<<'\n';return;
        }
        if (now>3*pw[2*n]) {
            dfs1(now-3*pw[2*n],x,y+pw[n],n-1);
        }else if (now>2*pw[2*n]) {
            dfs1(now-2*pw[2*n],x+pw[n],y,n-1);
        }else if (now>pw[2*n]) {
            dfs1(now-1*pw[2*n],x+pw[n],y+pw[n],n-1);
        }else dfs1(now,x,y,n-1);
    };
    function<void(int,int,int,int)>dfs2=[&](int x,int y,int n,int now) {
        if (n==-1) {
            cout<<now<<'\n';return;
        }
        if (x>pw[n]&&y>pw[n]) {
            dfs2(x-pw[n],y-pw[n],n-1,now+pw[2*n]);
        }else if (x>pw[n]) {
            dfs2(x-pw[n],y,n-1,now+2*pw[2*n]);
        }else if (y>pw[n]) {
            dfs2(x,y-pw[n],n-1,now+3*pw[2*n]);
        }else {
            dfs2(x,y,n-1,now);
        }
    };
    int n,q;cin>>n>>q;
    while (q--) {
        string s;cin>>s;
        if (s=="<-") {
            int d;cin>>d;
            dfs1(d,1,1,n);
        }else {
            int x,y;cin>>x>>y;
            dfs2(x,y,n,1);
        }
    }
}
signed main(){
    pw[0]=1;
    for (int i=1;i<=60;i++)pw[i]=pw[i-1]*2;
    ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
    int _=1;
    cin>>_;
    while(_--)solve();
}

E. Min Max MEX

\(过于简单 二分答案\)
\(因为连续 所以每次存满就清空即可\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define all(x) (x).begin(),(x).end()
#define rall(x) (x).rbegin(),(x).rend()
#define pb push_back
#define pii pair<int,int>
using namespace std;
const int mod=1e18;
int gcd(int a,int b){return b?gcd(b,a%b):a;};
int qpw(int a,int b){int ans=1;while(b){if(b&1)ans=ans*a%mod;a=a*a%mod,b>>=1;}return ans;}
int inv(int x){return qpw(x,mod-2);}
void solve(){
    int n,k;cin>>n>>k;
    vector<int>a(n+1);
    for (int i=1;i<=n;i++)cin>>a[i];
    auto ck=[&](int x) {
        vector<int>vis(x+1);
        int mex=0;
        int cnt=0;
        int pos=0;
        for (int i=1;i<=n;i++) {
            if (a[i]<=x)vis[a[i]]=i;
            while (mex<=x&&vis[mex]>pos)mex++;
            if (mex>=x)cnt++,mex=0,pos=i;
        }
        return cnt>=k;
    };
    int l=0,r=(n+1)/k+1;
    int ans;
    while (l<=r) {
        int mid=(l+r)>>1;
        if (ck(mid)) {
            ans=mid;
            l=mid+1;
        }else r=mid-1;
    }
    cout<<ans<<endl;
}
signed main(){
    ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
    int _=1;
    cin>>_;
    while(_--)solve();
}

F. Hackers and Neural Networks

\(除了题目难读一点没有难度 将最大个数匹配满 此时使用了n的次数\)
\(然后对于剩余的位置都需要先清空在加入即可\)
\(答案即为n+(n-mx)*2\)
\(特判不成立情况即可\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define all(x) (x).begin(),(x).end()
#define rall(x) (x).rbegin(),(x).rend()
#define pb push_back
#define pii pair<int,int>
using namespace std;
const int mod=1e18;
int gcd(int a,int b){return b?gcd(b,a%b):a;};
int qpw(int a,int b){int ans=1;while(b){if(b&1)ans=ans*a%mod;a=a*a%mod,b>>=1;}return ans;}
int inv(int x){return qpw(x,mod-2);}
void solve(){
    int n,m;cin>>n>>m;
    vector<vector<string>>b(m,vector<string>(n));
    vector<string>a(n);
    vector<int>vis(n);
    int mx=0;
    for (int i=0;i<n;i++)cin>>a[i];
    for (int i=0;i<m;i++) {
        int cnt=0;
        for (int j=0;j<n;j++) {
            cin>>b[i][j];
            if (b[i][j]==a[j])vis[j]=1,cnt++;
        }
        mx=max(mx,cnt);
    }
    for (int i=0;i<n;i++) {
        if (!vis[i]) {
            cout<<-1<<'\n';return;
        }
    }
    cout<<n+(n-mx)*2<<'\n';
}
signed main(){
    ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
    int _=1;
    cin>>_;
    while(_--)solve();
}

G. Shorten the Array

\(VP时被卡空间了(热)\)

Shorten the Array

题目描述

给定一个长度为 \(n\) 的数组 \(a\),定义子数组的 美丽值 \(f(b)\) 为子数组 \(b\) 中所有可能的数对 \((b_i, b_j)\) 的异或值的最大值,即:

\[f(b) = \max_{1 \leq i \leq j \leq m} (b_i \oplus b_j) \]

其中 \(\oplus\) 表示按位异或操作。

我们称一个子数组 \(b\)美丽的,如果其美丽值满足 \(f(b) \geq k\)。现在需要在一个数组 \(a\) 中找到最短的美丽子数组。如果不存在这样的子数组,则输出 \(-1\)


算法分析

关键问题

  1. 如何快速计算子数组的最大异或值

    • 暴力枚举所有子数组并计算最大异或值的时间复杂度为 \(O(n^3)\),显然不可行。
    • 我们需要一种高效的数据结构来维护当前子数组中的元素,并快速查询是否存在两个元素的异或值大于等于 \(k\)
  2. 如何判断异或值是否满足条件

    • 利用按位异或的性质:从高位到低位逐位匹配,可以快速判断是否存在满足条件的数对。
  3. 如何优化子数组长度的搜索

    • 使用滑动窗口思想,结合高效的查询数据结构(如 01 Trie),动态维护当前子数组的元素和查询结果。

核心思路

  1. 使用 01 Trie 维护子数组元素

    • 01 Trie 是一种二叉树结构,用于高效存储和查询二进制表示的数字。
    • 每次插入新元素时,将其二进制表示逐位插入到 Trie 中,同时记录每个节点对应的最新位置。
  2. 基于 Trie 的异或值查询

    • 对于当前元素 \(x\),从高到低逐位检查是否存在某个已插入的元素 \(y\),使得 \(x \oplus y \geq k\)
    • 如果 \(k\) 的某一位为 \(1\),则必须匹配该位为 \(1\) 的分支;否则优先尝试匹配该位为 \(1\) 的分支以最大化异或值。
  3. 滑动窗口更新最小长度

    • 动态维护当前子数组的最小长度,每次插入新元素后查询是否存在满足条件的数对,更新答案。
  4. 终止条件

    • 如果最终没有找到满足条件的子数组,则输出 \(-1\)

复杂度证明

  1. 时间复杂度

    • 每个元素插入到 01 Trie 中的时间复杂度为 \(O(31)\)(因为整数最多有 31 位)。
    • 查询操作同样为 \(O(31)\)
    • 总时间复杂度为 \(O(n \cdot 31) = O(n)\)
  2. 空间复杂度

    • 01 Trie 的节点总数最多为 \(O(n \cdot 31)\),因此空间复杂度为 \(O(n)\)

代码实现

以下是完整的 C++ 实现代码:

#include<bits/stdc++.h>
#define all(x) (x).begin(),(x).end()
#define rall(x) (x).rbegin(),(x).rend()
#define pb push_back
#define pii pair<int,int>
using namespace std;

void solve() {
    int n, k;
    cin >> n >> k;
    
    // 定义变量
    int cnt = 0; // 当前 Trie 节点计数
    auto get_pos = [&](int x, int y) {
        return (n * 31 + 1) * y + x; // 计算 Trie 节点的唯一标识
    };
    
    vector<int> nxt(n * 62 + 1); // 存储 Trie 的子节点指针
    vector<int> pos(n * 62 + 1); // 存储每个节点对应的位置
    
    // 插入函数:将元素 x 插入到 Trie 中
    auto insert = [&](int x, int P) {
        int p = 0; // 从根节点开始
        for (int i = 30; i >= 0; i--) { // 从高位到低位逐位插入
            int c = x >> i & 1; // 获取当前位的值
            if (!nxt[get_pos(p, c)]) nxt[get_pos(p, c)] = ++cnt; // 创建新节点
            p = nxt[get_pos(p, c)]; // 移动到子节点
            pos[p] = P; // 更新节点的最新位置
        }
    };
    
    // 查询函数:查找是否存在满足条件的数对
    auto get = [&](int x, int r) {
        int p = 0; // 从根节点开始
        int ok = 0; // 是否找到满足条件的数对
        int mi = n + 1; // 最小子数组长度
        
        for (int i = 30; i >= 0; i--) { // 从高位到低位逐位匹配
            int c = (x >> i & 1) ^ 1; // 尝试匹配异或值为 1 的分支
            if (k >> i & 1) { // 如果 k 的当前位为 1
                if (!nxt[get_pos(p, c)]) return mi; // 无法匹配,返回当前最小值
                p = nxt[get_pos(p, c)]; // 必须匹配该分支
            } else { // 如果 k 的当前位为 0
                if (nxt[get_pos(p, c)]) { // 如果存在异或值为 1 的分支
                    mi = min(mi, r - pos[nxt[get_pos(p, c)]] + 1); // 更新最小长度
                }
                if (!nxt[get_pos(p, c ^ 1)]) return mi; // 无法继续匹配,返回当前最小值
                p = nxt[get_pos(p, c ^ 1)]; // 移动到另一分支
            }
        }
        mi = min(mi, r - pos[p] + 1); // 更新最小长度
        return mi;
    };
    
    vector<int> a(n + 1); // 存储输入数组
    int len = n + 1; // 初始化最小长度为无穷大
    for (int r = 1; r <= n; r++) {
        cin >> a[r];
        insert(a[r], r); // 插入当前元素
        len = min(len, get(a[r], r)); // 查询并更新最小长度
    }
    
    if (len == n + 1) cout << -1 << '\n'; // 如果未找到满足条件的子数组
    else cout << len << '\n'; // 输出最小长度
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    int _ = 1;
    cin >> _;
    while (_--) solve();
}
posted @ 2025-04-09 18:56  archer2333  阅读(64)  评论(2)    收藏  举报