牛客竞赛 Round 44 C-F

前言

bilibili牛客竞赛官方视频讲解

  • 更为详细

正式内容

C - 连锁进位

思路:

  • 简单的模拟
  • 从后往前,每次加某个数使得该为变为0即可
  • 题目中n>1所以可以忽略n == 0的情况

第一种写法,建议跳过(太差)
以下是代码部分——本弱智写的代码(缺点:需要多次特判)

#include<bits/stdc++.h>
using namespace std;

void solve()
{
    string s;
    cin >> s;
    int ans = 0;
    int pos = (int)s.size() - 1;
    //查找第一个非零的位置,且不为最大的一位
    while(s[pos] == '0' && pos >= 1) pos --;
	//如果第一个非零的位置不是最高位
    if(pos != 0) ans = 10 - s[pos] + '0';
    //如果是最高位,或者最高位为0
    else
    {
    	//如果最高位为0
        if(s[pos] == '0')
            cout << "1\n";
        //如果最高位不是0
        else
            cout << "0\n";
        return ;
    }
    //该位置判断过了,判断下一个
    pos --;
    for(int i = pos; i >= 1; i --)
    {
    	//注意9的进位即可
        if(s[i] != '9')
            ans += 9 - s[i] + '0';
    }
    cout << ans << '\n';
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while(t --)
        solve();

    return 0;
}

第二种写法

以下是代码部分,代码参考来源——蒟蒻炖蒟蒻

#include<bits/stdc++.h>
using namespace std;

string s;

void solve()
{
    cin >> s;
    int n = (int)s.size();
    //ans存储结果,jw存储是否需要进位
    int ans = 0, jw = 0;
    for(int i = n - 1; i > 0; i --)
    {
    	//temp代表这一位上的数
        int temp = s[i] - '0' + jw;
        //如果temp不为0
        if(temp)
        {
        	//加上结果
            ans += 10 - temp;
            //并且需要进位
            jw = 1;
        }
        //如果无需进位
        else jw = 0;
    }
    cout << ans << '\n';
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while(t --)
        solve();

    return 0;
}

D - 因子区间

思路:

  • 1e5的数据,其因子数很小。利用这个性质,可以做以下前缀和
    • dp[i][j]表示从1~i区间中,因子数为j的数量
    • 之后通过等差数列求和公式,求出答案。

以下是代码部分

在这里#include<bits/stdc++.h>
using namespace std;

using ll = long long;
const int N = 2e5 + 10;

int a[N];
ll dp[N][170];

void solve()
{
    int n, q;
    cin >> n >> q;
    for(int i = 1; i <= n; i ++)
        cin >> a[i];

    for(int i = 1; i <= n; i ++)
    {
        int cnt = 0;
        //求因子数目
        for(int j = 1; j * j <= a[i]; j ++)
        {
            if(a[i] % j == 0)
            {
                cnt ++;
                if(a[i] / j != j) cnt ++;
            }
        }
        a[i] = cnt;
    }
	//先赋值
    for(int i = 1; i <= n; i ++)
        dp[i][a[i]] = 1;
    //前缀和的操作
    for(int i = 1; i < 170; i ++)
        for(int j = 2; j <= n; j ++)
            dp[j][i] += dp[j - 1][i];
            
    while(q --)
    {
        int l, r;
        cin >> l >> r;
        //记录答案
        ll ans = 0;
        for(int i = 1; i < 170; i ++)
        {
        	//前缀和
            ll temp = dp[r][i] - dp[l - 1][i];
            ans += temp * (temp - 1) / 2;
        }

        cout << ans << '\n';
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);cout.tie(nullptr);
    int t = 1;
    //cin >> t;
    while(t --)
        solve();

    return 0;
}

E - 小苯的排列构造

思路:

  • 1: 5 7 9 11 ……
  • 2: 6 8 10 12……
  • 3: 7 9 11 13……
  • 4: 8 10 12 14……
  • 5: 1 9 11 13……
  • 6: 2 10 12 14 ……
  • 7: 3 11 13 15 ……
  • 8: 4 12 14 16 ……
  • 9: 5 13 15……
  • 10: 6 14 16 ……
  • 得出归律:至少要8个数才能满足要求。大于8个数时,每4个数交换一下位置。
  • 比如:当n == 10
  • 5 6 7 8 1 2 3 4 9 10 ==> 5 6 7 8 9 10 3 4 1 2

以下是代码部分

#include<bits/stdc++.h>
using namespace std;

using ll = long long;
const int N = 2e5 + 10;

int a[N] = {0, 5, 6, 7, 8, 1, 2, 3, 4};

void solve()
{
    int n;
    cin >> n;
    if(n <= 7)
    {
        cout << "-1\n";
        return ;
    }
    for(int i = 9; i <= n; i ++)
    {
        a[i] = i;
        swap(a[i], a[i - 4]);
    }

    for(int i = 1; i <= n; i ++)
        cout << a[i] << " \n"[i == n];
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);cout.tie(nullptr);
    int t = 1;
    //cin >> t;
    while(t --)
        solve();

    return 0;
}

F - 小红的基环树删边

思路:

  • 来源题解——牛客288141082号的题解
  • 因为为基环树,所以从结点1到结点n的路径最多只有两条。
  • 每次遍历其中一条路径:
    • 那另一条没被走到的路径上的任一条边断开都是另一条路径的长度。
    • 如果f[i]始终为无穷,则代表第i条边断开,就无法从1结点走到n结点
    • 记得取最小值。

以下是代码部分,代码参考来源——牛客288141082号

  • 代码为他人代码,这里只是稍加解释
#include<bits/stdc++.h>
using namespace std;

using ll = long long;
const int N = 2e5 + 10;

//存储边的起点,终点和编号
vector<pair<int, int> > v[N];

int f[N];

bool vis[N];

int n;
//深搜
void dfs(int x, int dep)
{
	//停止标志,代表已走到终点n
    if(x == n)
    {
    	//给没有被遍历到的结点赋值
        for(int i = 1; i <= n; i ++)
            if(!vis[i])
                f[i] = min(f[i], dep);
        return ;
    }
    for(auto[y, id] : v[x])
        if(!vis[id])
        {
        	//标记为已走过该结点
            vis[id] = true;
            //路径长度+1
            dfs(y, dep + 1);
            //回溯
            vis[id] = false;
        }
}

void solve()
{
    cin >> n;
    for(int i = 1; i <= n; i ++)
    {
        int x, y;
        //初始化f[],赋值为无穷
        f[i] = 1e9;
        cin >> x >> y;
        //存入边(为无向图,存入两条)
        v[x].emplace_back(y, i);
        v[y].emplace_back(x, i);
    }
    dfs(1, 0);
    //输出
    for(int i = 1; i <= n; i ++)
    {
    	//无法到达,输出-1
        if(f[i] > 1e6) f[i] = -1;
        cout << f[i] << '\n';
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);cout.tie(nullptr);
    int t = 1;
    //cin >> t;
    while(t --)
        solve();

    return 0;
}
  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值