牛客寒假算法训练营2

A(签到)

B(贪心)

显然 当最大值的数目为奇数的时候 只有最大值可以留到最后 当最大值的数目为偶数的时候 显然除了最大值本身 任何数字都可以留到最后

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;
pair<int,int>a[N];
int b[N];
int n;
void solve(){
    cin>>n;
    int maxn=0;
    int cnt=0;
    for(int i=1;i<=n;i++){
        cin>>a[i].first;
        b[i]=a[i].first;
        maxn=max(maxn,a[i].first);
        a[i].second=i;
    }
    for(int i=1;i<=n;i++){
        if(a[i].first==maxn)cnt++;
    }
    if(cnt%2==1){
        for(int i=1;i<=n;i++){
            if(a[i].first==maxn)cout<<1;
            else cout<<0;
        }
        cout<<'\n';
    }else{
        for(int i=1;i<=n;i++){
            if(a[i].first==maxn)cout<<0;
            else cout<<1;
        }
        cout<<'\n';
    }
}
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;cin>>t;
    while(t--)solve();
    return 0;
}

C()

D()

E(构造)

给定一个整数n,要求构造一个n×n的01矩阵同时满足下列条件: 1. 每一行数字的和构成{0,1,2,3,··· ,n−1} 的数字集合。 2. 每一列数字的和构成{0,1,2,3,··· ,n−1} 的数字集合。 3. 数字连通块恰好有n个。 数据范围: 1≤n≤1000

题解:例如: n=6

000000

011111

010000

010111

010100

010101

考虑上图的矩阵,行从0∼n-1,列从0∼n-1,对于(i,j)的答案为 min(i,j) AND 1。 AND: 位运算中的按位与。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int n;
    cin>>n;
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            int num=min(i,j)&1;
            cout<<num;
        }
        cout<<'\n';
    }    
    return 0;
}

F(位运算 构造)

题目描述 给定一个整数n,构造两个不相等的整数x,y 满足gcd(x,y)=n 且 x ⊕y 最小。 数据范围: 1≤T ≤10^4,1≤n≤10^6

题解:gcd(x,y)=n 我们可以得出x ,y 是n的倍数 并且若x = an y=bn a 与 b互质

异或运算同为1减 不同则加 那么一定有|x −y|≤x ⊕y, x-y的最小值也就是(a-b)n 相邻两个数字一般互质 那么这个值就可以取到1 我们要构造两个数字 使得他俩异或的值等于相减的值也就是n 那么假设n二进制有k位 我们就可以取n<<k 和 (n<<k ) + n 这两个数字 高位直接减去 低位相加 这样结果就是n

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;
#define int long long
void solve(){
    int n;cin>>n;
    int k=1;
    while(k<=n){
        k<<=1;  
    }
    int x=n*k;
    int y=n*(k+1);
    cout<<x<<" "<<y<<'\n';
}
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;cin>>t;
    while(t--)solve();
    return 0;
}

H(贡献法)

定义一个数组的权值为其所有前缀中不同数字的个数,求所有子数组的 权值之和。

'题解 : 我们可以考虑贡献法 考虑一个数字能给多少个子数组贡献答案以及贡献的大小 当计算a[i]的时候一定包含i本身这个位置 那么我们要考虑 上一个a[i]的位置 j 那么左端点的选择方法有i-j个 右端点的选择方法就有n-i+1 种 那么也就是说 我们可以找到(i-j)*(n-i+1)个子数组 这些子数组又会有各自的前缀 只有覆盖i的前缀才能产生贡献 当左端点固定的时候 右端点产生的n-i+1个子数组 的有效前缀(覆盖位置i)分别有1 2 3 4 5 6 7 .。。(n-i+1) 个 那么也就是总共 (n−i+1)×(n−i+1+1)/2 个前缀 再乘上左端点的个数即可

代码:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
#define int long long
int a[N];
int n;
void solve(){
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    unordered_map<int,int>p;    //维护这个数字上一次出现的位置
    int ans=0;
    for(int i=1;i<=n;i++){
        int num=a[i];
        int pr=0;
        if(p.find(a[i])!=p.end()){
            pr=p[num];
        }
        int numl=i-pr;
        int numr=n-i+1;
        ans+=1LL*numl*numr*(numr+1)/2;
        p[num]=i;
    }
    cout<<ans<<'\n';
}
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;cin>>t;
    while(t--)solve();
    return 0;
}

I()

大致题意: 一个01矩阵 判断从每一个位置出发 是否都有一条路径 使得路径上所有字符构成的字符串是一个回文串

题解: 我们可以先找到一个终点 和起点相同 若没有显然不成立 找到终点后 我们可以向四周扩展 如果二者有相同的相邻字符 就可以扩展 如果没有 说明这个终点的四周的字符和终点本身相同 那么我们可以不断移动终点 使得二者有相同的相邻字符 不断找下去 就可以发现 只要有两个相同的点 那么最终一定会有一个回文串 只需要判断 0 和1 的数目就可以了

代码不再演示

J(图论、BFS、最短路)

要算出每个点前往更高等级的点的最短路 这里的等级 就是每个点的度数 最朴素的想法 我们可以对每个点进行bfs 这样一定可以解决问题 但是时间复杂度过高 o(nm);

想要优化 我们可以从高等级开始向低等级bfs 因为如果高等级bfs完后 当前等级仍然没有答案 那么他就一定不会有答案了 但是如果从低等级遍历 会导致重复 时间复杂度高

按照这种思路跑一边bfs即可

代码实现如下

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,m;
int deg[N];

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    vector<int>e[N];
    cin>>n>>m;

    for(int i=1;i<=m;i++){
        int u,v;cin>>u>>v;
        e[u].push_back(v);
        e[v].push_back(u);
        deg[u]++;deg[v]++;
    }    
    map<int,deque<int>>mp;
    for(int i=1;i<=n;i++){
        mp[deg[i]].push_back(i);
    }
    vector<int>dis(n+1,1e9),ans(n+1);
    for(auto it=mp.rbegin();it!=mp.rend();it++){
        auto x=it->first;
        auto &q=it->second;
        for(auto &i :q){
            if(dis[i]>1e8)dis[i]=-1;
            ans[i]=dis[i];
            dis[i]=0;
        }
        while(q.size()){
            auto u=q.front();
            q.pop_front();
            for(auto v:e[u]){
                if(deg[v]>=x)continue;
                if(dis[v]>dis[u]+1){
                    dis[v]=dis[u]+1;
                    q.push_back(v);
                }
            }
        }
    }
    for(int i=1;i<=n;i++){
        cout<<ans[i]<<' ';
    }
    cout<<'\n';
    return 0;
}
相关推荐
Σίσυφος190013 小时前
ICP 为啥会陷入到局部?为何point to Plane 比point to Point 收敛更快?
算法
C雨后彩虹13 小时前
最小矩阵宽度
java·数据结构·算法·华为·面试
liuyao_xianhui13 小时前
动态规划_最长递增子序列_C++
java·开发语言·数据结构·c++·算法·链表·动态规划
不想看见40413 小时前
Find All Numbers Disappeared in an Array数组--力扣101算法题解笔记
笔记·算法·leetcode
月明长歌13 小时前
【码道初阶-Hot100】LeetCode 438 + 567 对照详解:一套滑动窗口模板,彻底讲透“固定长度窗口 + 计数数组 + count维护”
算法·leetcode·滑动窗口
旖-旎13 小时前
二分查找(搜索插入位置)(3)
c++·算法·二分查找·力扣·双指针
XW010599913 小时前
5-11字典合并
数据结构·python·算法
wunianor13 小时前
[算法]2026年3月14日米哈游校招算法笔试题题解
算法
Elias不吃糖13 小时前
LeetCode-44 回溯解法
算法·leetcode·职场和发展
仟濹13 小时前
【算法打卡day25(2026-03-17 周二)今日算法:「回溯算法」】1-力扣17-电话号码的字母组合 2-力扣39-组合总和 3-力扣40-组合总和II
算法·leetcode·职场和发展