【题解】2020牛客NOIP赛前集训营-普及组(第四场)

A - 时间

题目大意

给出一个时间,求出在24小时制下的3小时30分后的时间为多少。

简要题解

直接模拟即可,注意两天间时间的变化。

具体可以把时间都换算成分钟,然后对一天的分钟数取模,然后再换算成标准时间。

参考代码

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

int main()
{
    int h, m;
    scanf("%d:%d", &h, &m);
    m = h * 60 + m;
    m += 3 * 60 + 30;
    m %= 24 * 60;
    h = m / 60, m = m % 60;
    printf("%02d:%02d\n", h, m);
    return 0;
}

B - 石子

题目大意

给出堆石子,Alice每次可以从一堆石子中取偶数个石子,Bob每次可以从一堆中取奇数个石子,每次操作至少要取走一个石子,无法操作的人输掉游戏。假设Alice和Bob都是绝顶聪明的,请问Alice先手时,Alice是否有必胜策略。

简要题解

结论:当且仅当石子堆数为1堆,且石子数量为偶数时,Alice有必胜策略,否则一定是Bob获胜。

证明:Bob的策略为:若场上存在偶数堆石子,那么将这一堆取走奇数个,使其变成奇数堆石子;否则,取走一整堆石子(因为此时每一堆石子数量均为奇数)。由此可见,无论Alice如何操作,场上的偶数石子堆的数量一直减少,因而无法使得Bob无法操作。

参考代码

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

int main()
{
    int n;
    while (~scanf("%d", &n))
    {
        int d;
        for (int i = 1; i <= n; i++)
        {
            scanf("%d", &d);
        }
        if (n == 1 && d % 2 == 0)
            puts("YES");
        else
            puts("NO");
    }
    return 0;
}

C - 卡片

题目大意

给出两个正多边形,其中一个绕着另一个旋转,求回到原来位置时所需要的旋转次数。

简要题解

考虑将旋转操作分为两类:

第一类是在地面上旋转,如样例中的第一次旋转;

第二类是在角上旋转,如样例中的第二次旋转。

然后分别讨论两类旋转的次数,求和输出即可。

参考代码

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

typedef long long ll;

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    ll a, m, b, n;
    while (cin >> a >> m >> b >> n)
    {
        ll x = a * (n * b) / __gcd(a, n * b);
        ll c1 = x / a;
        ll y = a * b / __gcd(a, b);
        ll c2 = x / y * (y / b - 1);
        cout << c1 + c2 << '\n';
    }
    return 0;
}

D - 项链

我们可以称项链为点的子段和。记最大子段和为,次大子段和为 .

如何求最大值

对于 其实我们已经有了明确的解法。
设当前结点的编号为,我们可以维护两个值 分别表示从起点到 的前缀和的最小值以及到 的前缀和。那么显然我们用 去更新 就可以得到正解。

如何求次大值

然后我们也应该注意到,最大子段和必然是一条连续的路径,也意味着它存在起点 和终点 。所以 出现的区间必然在 的左侧或者 的右侧。

对于链的情况

我们只需要在更新 的时候更新出 ,更新 的时候更新 ,之后注意边界重复两次即可(时间是允许的)。此外,由于我们在每个点都会用 更新 ,我们可以额外变量 维护一个从起点到 的所有 的最大值,以降低复杂度。

对于 的情况

由于图中没有环,那么我们从任意一个点 都可以得到若干条链,不难转换为情况

对于完整的数据

我们首先需要明确,利用拓扑序得到的答案肯定不会比从某个点暴力 得到的答案更差,且利用拓扑序我们化解的图的问题,如此我们就有个拓扑序上 的想法。
仍然需要维护 这些变量。不同的是在更新 时需要利用 更新 ,选取 更大的路径。
对于 之后的路径。
最后由于在图中可能得到很多处,我们记录所有出现的位置,以每个出现的位置 为起点记忆化搜索跑出一个最大值即可。

参考代码

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

typedef long long ll;
const int maxn = 100010;
const ll inf = 0x3f3f3f3f3f3f3f3f;

int deg[maxn], pre[maxn];
ll w[maxn], mnsum[maxn], mxsum[maxn], sum[maxn], val[maxn], dp[maxn];
vector<int> v[maxn];

queue<int> q;
vector<int> pos;

ll ans1 = 0, ans2 = 0;

inline void dfs(int x, ll pmnsum, ll psum)
{
    ans2 = max(ans2, psum - pmnsum);
    if (psum - pmnsum < dp[x]) return;
    dp[x] = psum - pmnsum;
    for (int i = 0; i < v[x].size(); ++i) {
        dfs(v[x][i], min(pmnsum, psum + w[v[x][i]]), psum + w[v[x][i]]);
    }
}

int main()
{
    int n, m;
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; ++i) {
        scanf("%lld", &w[i]);
    }
    for (int i = 1, x, y; i <= m; ++i) {
        scanf("%d %d", &x, &y);
        v[x].push_back(y);
        ++deg[y];
    }
    for (int i = 1; i <= n; ++i) {
        val[i] = sum[i] = -inf;
    }
    for (int i = 1; i <= n; ++i) {
        if (deg[i] == 0) {
            q.push(i);
            mnsum[i] = min(0LL, w[i]);
            sum[i] = w[i];
            val[i] = max(0LL, w[i]);
            pre[i] = (w[i] > 0 ? 0 : i);
            ans1 = max(ans1, val[i]);
        }
    }
    while (q.size()) {
        int now = q.front(); q.pop();
        for (int i = 0; i < v[now].size(); ++i) {
            int to = v[now][i];
            if (val[to] <= sum[now] - mnsum[now] + w[to]) {
                val[to] = sum[now] - mnsum[now] + w[to];
                sum[to] = sum[now] + w[to];
                pre[to] = (sum[to] > mnsum[now] ? pre[now] : to);
                mnsum[to] = min(mnsum[now], sum[to]);
                mxsum[to] = max(mxsum[now], sum[to] - mnsum[to]);
                if (val[to] > ans1) {
                    ans1 = val[to], ans2 = mxsum[pre[to]];
                    pos.clear();
                    pos.push_back(to);
                } else if (val[to] == ans1) {
                    ans2 = max(ans2, mxsum[pre[to]]);
                    pos.push_back(to);
                }
            }
            --deg[to];
            if (deg[to] == 0) {
                q.push(to);
            }
        }
    }
    for (int i = 0; i < pos.size(); ++i) {
        for (int j = 0; j < v[pos[i]].size(); ++j) {
            dfs(v[pos[i]][j], 0, 0);
        }
    }
    printf("%lld %lld\n", ans1, ans2);
    return 0;
}
全部评论
因而无法使得Bob无法操作?,不该是A吗
1 回复 分享
发布于 2020-10-25 14:27
C题的推导过程能给一下吗?初中生无法理解
点赞 回复 分享
发布于 2020-10-25 21:30
C题是什么玩意儿,能再详细一点吗,最好有个证明
点赞 回复 分享
发布于 2020-10-25 11:46
c题为什么我这个最后两个点还是没过? https://ac.nowcoder.com/acm/contest/view-submission?submissionId=45324881
点赞 回复 分享
发布于 2020-10-25 09:53
D 是咋回事。
点赞 回复 分享
发布于 2020-10-24 22:05

相关推荐

最近群里有很多同学找我看简历,问问题,主要就是集中在明年三月份的暑期,我暑期还能进大厂嘛?我接下来该怎么做?对于我来说,我对于双非找实习的一个暴论就是title永远大于业务,你在大厂随随便便做点慢SQL治理加个索引,可能就能影响几千人,在小厂你从零到一搭建的系统可能只有几十个人在使用,量级是不一样的。对双非来说,最难的就是约面,怎么才能被大厂约面试?首先这需要一点运气,另外你也需要好的实习带给你的背书。有很多双非的同学在一些外包小厂待了四五个月,这样的产出有什么用呢?工厂的可视化大屏业务很广泛?产出无疑是重要的,但是得当你的实习公司到了一定的档次之后,比如你想走后端,那么中厂后端和大厂测开的选择,你可以选择中厂后端(注意,这里的中厂也得是一些人都知道的,比如哈啰,得物,b站之类,不是说人数超过500就叫中厂),只有这个时候你再去好好关注你的产出,要不就无脑大厂就完了。很多双非同学的误区就在这里,找到一份实习之后,就认为自己达到了阶段性的任务,根本不再投递简历,也不再提升自己,玩了几个月之后,美其名曰沉淀产出,真正的好产出能有多少呢?而实际上双非同学的第一份实习大部分都是工厂外包和政府外包!根本无产出可写😡😡😡!到了最后才发现晚了,所以对双非同学来说,不要放过任何一个从小到中,从中到大的机会,你得先有好的平台与title之后再考虑你的产出!因为那样你才将将能过了HR初筛!我认识一个双非同学,从浪潮到海康,每一段都呆不久,因为他在不断的投递和提升自己,最后去了美团,这才是双非应该做的,而我相信大部分的双非同学,在找到浪潮的那一刻就再也不会看八股,写算法,也不会打开ssob了,这才是你跟别人的差距。
迷茫的大四🐶:我也这样认为,title永远第一,只有名气大,才有人愿意了解你的简历
双非本科求职如何逆袭
点赞 评论 收藏
分享
想进开水团喝开水:哦 给我一个 就算你真拿到牛友也会为你开心的
点赞 评论 收藏
分享
评论
1
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务