大连大学2022 4月程序设计竞赛

这篇博客探讨了在信息技术领域中,数学和算法如何被巧妙运用。内容包括E数圆圈问题的规律模拟,图论在路径查找中的应用,寻找最大差值的策略,以及异或操作在数字处理中的优化。通过实例解析了如何利用数学和算法解决复杂问题,提高程序效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

E 数圆圈(规律 模拟)

Description:

​ 定义f(x) = x里各位圆圈数量之和

​ 现在给定x 对x进行k次f(x)操作 返回结果

​ 0, 6, 9 = 1; 8 = 2;

Solution:

​ 那我们考虑最坏的情况 那就是17位上面都是8 那也是17*2 会化成一个很小的数

​ 我们就可以知道 对于一个x 我们可以通过数十次次迭代之内 将他变成01的循环态

​ 于是我们模拟即可 (用字符串不会挂 但是用整数是会挂的 因为x可能是负数

Code:

LL get(LL x)
{
    LL res = 0;
    string s = to_string(x);
    for(int i = 0; i < s.size(); i++)
    {
        if(s[i] == '8')    res += 2;
        if(s[i] == '0' || s[i] == '6' || s[i] == '9')    res ++;
    }
    return res;
}

void solve()
{
    LL x, k;
    cin >> x >> k;
    LL res = x;
    while(k)
    {
        res = get(res);
        k --;
        if(res == 0 || res == 1)    break;
    }
    
    if(res == 1 && k)
    {
        if(k & 1)    res = 0;
        else res = 1;
    }
    else if(res == 0 && k)
    {
        if(k & 1)    res = 1;
        else res = 0;
    }
    
    cout << res << endl;
}

F 旅行(图论)

Description:

​ 给出一个图 然后询问一下点a在不在1到n的最短路径上

Solution:

​ 方法1:用佛洛依德跑一遍 判断g[1] [a] + g[a] [n] ?= g[1] [n]

​ 方法2:首先用迪杰斯特拉从1跑到n 得到从1为起点到各个点的最短路

​ 然后再建反边 从n开始跑到1 得到以n为起点到各个点的最短路

Code:

#include <bits/stdc++.h>

using namespace std;
#define rep(i, a, n) for(int i = a; i < n; i++)
#define per(i, a, n) for(int i = n - 1; i >= a; i--)
#define fi first
#define se second
#define pb push_back
#define ios ios::sync_with_stdio(false);cin.tie(0);
#define endl '\n'
#define ms(x, y)    memset(x, y, sizeof x)
typedef double db;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1010, M = 3010;

int n, m, Q;
int dis1[N];
int dis2[N];
bool st[N];

int h[N], e[M], val[M], nex[M], idx;

void add(int a, int b, int c)
{
    e[idx] = b;
    nex[idx] = h[a];
    val[idx] = c;
    h[a] = idx ++;
}

void dijkstra1()
{
    ms(dis1, 0x3f);
    priority_queue<PII, vector<PII>, greater<PII> > q;
    q.push({0, 1});
    dis1[1] = 0;
    
    while(q.size())
    {
        PII t = q.top();
        q.pop();
        
        int id = t.se, dist = t.fi;
        if(st[id])    continue;
        st[id] = true;
        
        for(int i = h[id]; i != -1; i = nex[i])
        {
            int j = e[i];
            if(dis1[j] > val[i] + dist)
            {
                dis1[j] = val[i] + dist;
                q.push({dis1[j], j});
            }
        }
    }
}

void dijkstra2()
{
    ms(dis2, 0x3f);
    priority_queue<PII, vector<PII>, greater<PII> > q;
    q.push({0, n});
    dis2[n] = 0;
    
    while(q.size())
    {
        PII t = q.top();
        q.pop();
        
        int id = t.se, dist = t.fi;
        if(st[id])    continue;
        st[id] = true;
        
        for(int i = h[id]; i != -1; i = nex[i])
        {
            int j = e[i];
            if(dis2[j] > val[i] + dist)
            {
                dis2[j] = val[i] + dist;
                q.push({dis2[j], j});
            }
        }
    }
}
    
int main()
{
    ms(h, -1);
    scanf("%d%d%d", &n, &m, &Q);
    
    vector<int> u(m), v(m), c(m);
    for(int i = 0; i < m; i++)    
        scanf("%d%d%d", &u[i], &v[i], &c[i]);
    
    for(int i = 0; i < m; i++)
        add(u[i], v[i], c[i]);
    
    dijkstra1();

    ms(h, -1);
    ms(st, false);
    idx = 0;
    
    for(int i = 0; i < m; i++)
        add(v[i], u[i], c[i]);
    
    dijkstra2();

    while(Q --)
    {
        int l;
        scanf("%d", &l);
        if(dis1[l] + dis2[l] == dis1[n])   
            puts("yes");
        else puts("no");
    }
    return 0;
}

H 寻找最大差值(思维)

Description:

​ 给定数组a 求a_j - a_i (且a_j > a_i) 的最大值 要求i < j 如果这个数不存在就输出-1

Solution:

​ 一开始就各种二分 排序 各种想 就是想不到怎么写 那其实可以用On写过

​ 我们倒序循环j来维护一个mx=max(a[j]) 然后用mx去减去比他更小的a[i] 这时候必定存在i < j

Code:

int a[100010];
void solve()
{
    int n;
    cin >> n;
    rep(i, 1, n + 1)
        cin >> a[i];
    
    int mx = 0;
    int res = -1;
    per(i, n, 1)
    {
        mx = max(mx, a[i]);
        if(mx - a[i] > 0)   res = max(mx - a[i], res);
    }        
    cout << res << '\n';
}

H Raksasa的轻功(读题)

Description:

​ 给定数组a 可以从任一点开始找最长连续下降序列(可以向左或者向右)

Solution:

​ 我们可以从两个端点向另一点扫 因为可以连续向左也可以连续向右

​ 然后 我们就On求一下两次最长连续下降序列

​ 还有一种做法就是从前扫到后 记录最长连续下降序列和最长连续上升序列(倒过来做)

Code:

int a[200010];
int main()
{
    int n;
    cin >> n;
    rep(i, 1, n + 1)
        cin >> a[i];

    int up = 0, down = 0, res = 0;
    rep(i, 1, n)
    {
        if(a[i] < a[i + 1])    res = max(res, ++ up), down = 0;
        else if(a[i] > a[i + 1])    res = max(res, ++ down), up = 0;        
        else if(a[i] == a[i + 1])    up = 0, down = 0;
    }
    cout << res << endl;
}

L 函数求和(思维)

Description:

题目就是这个意思
∑ i = 1 n m i n L i < = j < = i { g c d ( i , j ) }   m o d ( 1 0 9 + 7 ) \sum_{i=1}^n min_{L_i<=j<=i}\left\{gcd(i,j)\right\}\ mod (10^9+7) i=1nminLi<=j<=i{gcd(i,j)} mod(109+7)
Solution:

​ 因为保证L_i <= j <= ii = L_i的时候 j只能选L_i这点我们是可以确认的

​ 那如何取最小的gcd呢 那我们可以知道gcd(x, x - 1) = 1

​ 所以 我们想到辣 当i != L_i的时候 我们直接选gcd(i, i - 1)就可以了

Code:

int a[10000010];

int main()
{
    int n = read(), m = read();
    for(int i = 1; i <= n; i++)
        a[i] = read();
    
    LL res = 0;
    while(m --)
    {
        int x = read();
        if(x == a[x])   res = (res + x) % mod;
        else res = (res + 1) % mod;
    }
    cout << res;
    return 0;
}

N Raksasa的数字(构造)

Description:

​ 给定数组a 我们可以选择一个数x来对数组内的数全部进行异或

​ 最后要使得数组和最小 当答案有多个x的时候 我们要选择小的x

Solution:

​ 因为异或 让相同的变成0 不同的变成1 所以如果对于一位数来说

​ 0多的话就让这一位异或0 1多的话就让这一位异或1

​ 贪心地构造一下吧 就对于每个数 将其化为二进制格式 然后统计一下每一位是0的个数多还是1的个数多

​ 那什么时候会出现多个x呢 就是当一位数上0和1个数相等 这个时候 异或0和1都是一样的结果 但是因为为了保证x最小 这时候要异或0

​ trick:用bitset可以减少时间复杂度

Code:

int cnt[32];//每一位1的数量
void solve()
{
    vector<bitset<32>> a;
    int n;
    cin >> n;
    rep(i, 0, n)
    {
        int x;
        cin >> x;
        bitset<32> bi(x); //把x化为01串
        a.pb(bi);
    }
    
    ms(cnt, 0);
    rep(i, 0, n)
    {
        rep(j, 0, 32)
            cnt[j] += a[i][j];
    }
    
    LL res = 0;
    rep(i, 0, 32)
    {
        if(cnt[i] > n / 2) //尽量小 所以0和1个数相等的时候不加上这一位
            res += (1LL << i);
    }
    cout << res << endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值