NEUQ-ACM week5

新国王游戏

思路:

将每个人的得分拆分为两部分,左边的乘积和右边的数字,分别计算出每个人左边的乘积,然后再按右边的数字从大到小排序,这样就可以保证每个人右边的乘积都是比他大的数的乘积,只需要依次计算每个人的得分并求和即可。

代码:

#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
const int N = 1e6 + 10;
struct Node {
    int a, b, l;
} tr[N];

bool cmp(Node& x, Node& y) {
    return x.b > y.b;
}

int main() {
    int n;
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> tr[i].a >> tr[i].b;
        tr[i].l = i;
    }

    sort(tr, tr + n, cmp);

    int s = 1, res = 0;
    for (int i = 0; i < n; i++) {
        res = (res + (long long)s * tr[i].a) % mod;
        s = (long long)s * tr[i].b % mod;
    }

    cout << res << endl;

    return 0;
}

 完美数

思路:

因为 m 很大,所以我们需要对答案取模。显然,对于一个 m 位数,如果该数每一位都是 a 或者 b,那么该数就是好数。因此,我们可以先计算出有多少个 m 位数是好数,再遍历所有好数,统计完美数的数量。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 1000010, mod = 1e9 + 7;
int a, b, m;
int f[N], g[N];
int get(int x)
{
    int res = 0;
    while (x) res += x % 10, x /= 10;
    return res == a || res == b;
}

int qmi(int a, int b)
{
    int res = 1 % mod;
    for (; b; b >>= 1)
    {
        if (b & 1) res = (long long)res * a % mod;
        a = (long long)a * a % mod;
    }
    return res;
}

int main()
{
    cin >> a >> b >> m;

    f[0] = 1;
    for (int i = 1; i <= m; i ++ )
    {
        f[i] = (long long)(f[i - 1] * 8ll + qmi(10, i - 1)) % mod;
        g[i] = (long long)(g[i - 1] * 10ll + 1) % mod;
    }

    int res = 0;
    for (int i = 1; i <= m; i ++ )
        if (get(i)) res = (res + (long long)f[m - i] * g[i - 1] % mod) % mod;

    cout << res << endl;

    return 0;
}

 Lusir的游戏

 思路:

题目要求求出至少以多少能量值开始游戏,才可以保证成功完成游戏。本题是一个求解问题,且只要求解一个值,因此可以考虑使用二分查找进行求解。

对于一个给定的能量值 E,我们可以通过模拟游戏的过程,判断该能量值是否可以完成游戏,如果可以则二分查找更小的能量值,否则二分查找更大的能量值,直到找到最小的能量值 E 使得可以完成游戏。

具体实现中,我们可以考虑对于每一个能量值 E,从第 0 号建筑出发,模拟游戏的过程,看是否可以到达第 N 号建筑。具体来说,从第 0 号建筑开始,每次跳到下一个建筑,计算下一个建筑与当前建筑之间需要的能量值,然后判断是否能够到达下一个建筑即可。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int h[N];
int n;
int main()
{
    cin >> n;
    for (int i = 1; i <= n; i ++ ) cin >> h[i];

    int res = 0;
    for (int i = n; i; i -- ) {
        if (h[i] > res) res = (h[i] + res + 1) / 2;
    }

    cout << res << endl;

    return 0;
}

BFS练习1

 

思路:

这是一个广搜的问题,我们可以从a开始进行广搜,将a的每个操作(加1,乘2,乘3,减1)所得的结果存入队列中,然后逐层进行广度优先搜索,直到找到目标数b为止,期间记录步数即可。

具体来说,每次从队列中取出一个元素进行判断,如果与目标数相等,那么就是最短步数,否则就将这个元素的所有操作结果存入队列中,同时将步数加1,继续进行搜索,直到队列为空或者找到目标数为止。

代码:

include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int a, q;
int b[N];

int bfs(int n)
{
    queue<int> q;
    int d[N];
    memset(d, -1, sizeof d);

    q.push(n);
    d[n] = 0;

    while (q.size())
    {
        int t = q.front();
        q.pop();

        if (t == a) return d[t];

        if (t + 1 < N && d[t + 1] == -1)
        {
            q.push(t + 1);
            d[t + 1] = d[t] + 1;
        }

        if (t * 2 < N && d[t * 2] == -1)
        {
            q.push(t * 2);
            d[t * 2] = d[t] + 1;
        }

        if (t * 3 < N && d[t * 3] == -1)
        {
            q.push(t * 3);
            d[t * 3] = d[t] + 1;
        }

        if (t - 1 >= 0 && d[t - 1] == -1)
        {
            q.push(t - 1);
            d[t - 1] = d[t] + 1;
        }
    }

    return -1;
}

int main()
{
    cin >> a >> q;

    for (int i = 0; i < q; i ++ ) cin >> b[i];

    for (int i = 0; i < q; i ++ )
    {
        int res = bfs(b[i]);
        cout << res << ' ';
    }

    return 0;
}

01序列2

思路:

我们考虑如何在长度为 $n$ 的 01 序列中选取两个相邻的 1,使得它们之间至少有 $k$ 个 0。我们先选取这两个 1,它们的距离为 $d$,则它们之间有 $d-1$ 个 0,那么我们还需要在 $n-d$ 个位置中再选择 $k-(d-1)$ 个 0。

于是,问题转化为从长度为 $n-d$ 的 01 序列中选取 $k-(d-1)$ 个 0 的方案数,即从 $n-d$ 个不同的物品中选取 $k-(d-1)$ 个不同的物品的方案数,即$C_{n-d}^{k-(d-1)}$。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10, MOD = 1e9 + 7;
int fac[N], inv[N];

int qmi(int a, int b) {
    int res = 1 % MOD;
    while (b) {
        if (b & 1) res = (long long)res * a % MOD;
        a = (long long)a * a % MOD;
        b >>= 1;
    }
    return res;
}

void init(int n) {
    fac[0] = 1;
    for (int i = 1; i <= n; i++)
        fac[i] = (long long)fac[i - 1] * i % MOD;
    inv[n] = qmi(fac[n], MOD - 2);
    for (int i = n - 1; i >= 0; i--)
        inv[i] = (long long)inv[i + 1] * (i + 1) % MOD;
}

int C(int a, int b) {
    if (a < b) return 0;
    return (long long)fac[a] * inv[b] % MOD * inv[a - b] % MOD;
}

int main() {
    int n, k;
    cin >> n >> k;

    init(n);

    int ans = 0;
    for (int i = k; i <= n; i++)
        ans = (ans + (long long)C(i - 1, k - 1) * C(n - i + 1, k) % MOD) % MOD;

    cout << ans << endl;

    return 0;
}

整除光棍

思路:

本题需要求得满足条件的最小的s和n,因此可以采用逐位扩展的方法来判断,当满足条件的时候,就可以输出答案了。

具体来说,我们可以先构造一个由1组成的数字,然后通过取模运算,判断该数字是否能够整除给定的奇数x,如果不能整除,就将该数字乘以10并加上1,继续判断,直到满足条件为止。

如何判断是否满足条件呢?因为要求s最小,所以每次判断的时候,我们选择对当前的数x除以该数字,得到一个余数y,然后如果y为0,说明找到了一个满足条件的s和n,否则我们就将该数字乘以10并加上1,继续判断即可。

代码: 

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

LL get_s(int n) {
    LL res = 1;
    string s;
    while (s.size() <= n) {
        s += '1';
        res = stoll(s) / n;
    }
    return res;
}

int get_n(LL s) {
    int res = 0;
    while (s) {
        res++;
        s /= 10;
    }
    return res;
}

int main() {
    int x;
    cin >> x;

    LL s = get_s(x);
    int n = get_n(s);

    cout << s << " " << n << endl;

    return 0;
}

碰撞2

 思路:

用两个数组记录每个人在横坐标和纵坐标上前面第一个人和后面第一个人的位置,若某个人的前面或后面没有人,则用 0 表示。在根据每个人的方向来更新前后位置数组。遍历每个人,若前后位置数组中坐标差小于当前位置之间的坐标差,则有碰撞。

代码:

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

const int N = 2e5 + 10;

struct Person
{
    int x, y;
    int id;
    char dir;
}p[N];

int n;
int vis[N];
char s[N];

bool cmp(Person& p1, Person& p2)
{
    if (p1.y != p2.y) return p1.y < p2.y;
    return p1.x < p2.x;
}

bool check()
{
    vector<int> stk;

    for (int i = 1; i <= n; i ++ )
    {
        if (s[p[i].id] == 'R')
        {
            stk.push_back(i);
        }
        else if (s[p[i].id] == 'L')
        {
            if (stk.empty()) continue;
            int j = stk.back(); stk.pop_back();
            if ((p[i].x - p[j].x) % 2) return true;
            vis[i] = vis[j] = 1;
        }
    }

    for (int i = 1; i <= n; i ++ )
        if (!vis[i]) return false;
    return true;
}

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i ++ )
    {
        cin >> p[i].x >> p[i].y;
        p[i].id = i;
    }
    cin >> s + 1;

    sort(p + 1, p + n + 1, cmp);

    if (check()) puts("Yes");
    else puts("No");

    return 0;
}

 优美!最长上升子序列

思路:

我们可以利用动态规划来解决该问题。具体地,我们定义一个 dp 数组,其中 dp[i] 表示以第 i 个数结尾的递增子序列的最大长度。

那么我们可以通过状态转移方程来计算 dp[i] 的值,即 dp[i] = max{dp[j] + 1},其中 j 是 i 的因数且 nums[j] < nums[i]。

最终,我们可以通过遍历 dp 数组来找到最大值,即为所求的结果。

代码:

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

const int N = 1e6 + 10;

int a[N], dp[N], cnt[N];
int n;

int main()
{
    int T;
    cin >> T;
    while (T--)
    {
        cin >> n;
        for (int i = 1; i <= n; i++) cin >> a[i];

        int res = 0;
        memset(dp, 0, sizeof dp);
        memset(cnt, 0, sizeof cnt);
        for (int i = 1; i <= n; i++)
        {
            dp[i] = 1;
            cnt[i] = 1;
            for (int j = 1; j < i; j++)
                if (a[i] % a[j] == 0)
                {
                    if (dp[j] + 1 > dp[i])
                    {
                        dp[i] = dp[j] + 1;
                        cnt[i] = cnt[j];
                    }
                    else if (dp[j] + 1 == dp[i])
                    {
                        cnt[i] += cnt[j];
                    }
                }

            if (dp[i] > res) res = dp[i];
            else if (dp[i] == res) cnt[res] += cnt[i];
        }

        cout << cnt[res] << endl;
    }

    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值