Educational Codeforces Round 103 (Rated for Div. 2)部分题解(A,B,C,D)

oj: CodeForces

叙述采用倒叙

D. Journey(dp)

oj: CodeForces

题意

n + 1 n+1 n+1 个点和 n n n 条边组成的一条链,链上的边只能有一个朝向(向左或者向右)。

有个旅行者从一个点出发后只能顺着边的朝向移动,不过每当他移动一格,所有边的朝向都会改变(朝左的变成朝右,朝右的变成朝左)。

求出旅行者分别在每一个点出发能够走到的最多的点数。

题解

定义 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]

i i i 0 0 0 时表示只向左移动,为 1 1 1 时表示只向右移动。

j j j 0 0 0 时表示边是初始朝向,为 1 1 1 时表示边的朝向发生了改变。

k k k 表示以第 k k k 个点为起点。

d p dp dp 值表示第 k k k 个点只向左(或者只向右,取决于 i i i )能够走到的最多的点数。

边界:

所有的点都不移动,即走过的点只有自己: d p [ i ] [ j ] [ k ] = 1 dp[i][j][k] = 1 dp[i][j][k]=1

转移:

考虑旅行者只向左移动时(即 i = 0 i=0 i=0 ),当左邻边朝向左,旅行者只能在朝向未改变时通过。当左邻边朝向右,旅行者只能在朝向改变时通过。

所以有:

if(s[i - 1] == 'L') dp[0][0][i] += dp[0][1][i - 1];
else dp[0][1][i] += dp[0][0][i - 1];

考虑旅行者只向右移动时(即 i = 1 i=1 i=1 ),当右邻边朝向左,旅行者只能在朝向改变时通过。当右邻边朝向右,旅行者只能在朝向未改变时通过。

所以有:

if(s[i] == 'R') dp[1][0][i] += dp[1][1][i + 1];
else dp[1][1][i] += dp[1][0][i + 1];

因为向左和向右时我们都计算了本身的贡献,所以第 i i i 位的答案应该是 d p [ 0 ] [ 0 ] [ i ] + d p [ 1 ] [ 0 ] [ i ] dp[0][0][i]+dp[1][0][i] dp[0][0][i]+dp[1][0][i] -1。

代码

#include <bits/stdc++.h>
#define _for(i, a) for(int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(int i = (a), lennn = (b); i <= lennn; ++i)
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n"
using namespace std;
typedef long long LL;
const int maxn = 300005;

inline int read() {
    int x(0), f(1); char ch(getchar());
    while (ch<'0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

int n;
char s[maxn];
LL dp[2][2][maxn];

void init() {
    _for(j, 2) _for(i, n + 3) dp[j][0][i] = dp[j][1][i] = 0;
}

void sol() {
    init();
    scanf("%s", s + 1);
    _rep(i, 1, n + 1) dp[0][0][i] = dp[0][1][i] = 1;
    _rep(i, 2, n + 1) {
        if(s[i - 1] == 'L') dp[0][0][i] += dp[0][1][i - 1];
        else dp[0][1][i] += dp[0][0][i - 1];
    }
    _rep(i, 1, n + 1) dp[1][0][i] = dp[1][1][i] = 1;
    for(int i = n; i >= 1; --i) {
        if(s[i] == 'R') dp[1][0][i] += dp[1][1][i + 1];
        else dp[1][1][i] += dp[1][0][i + 1];
    }
    _rep(i, 1, n + 1) printf("%lld ", dp[0][0][i] + dp[1][0][i] - 1);
    printf("\n");
}

int main() {
    int T = read();
    _for(i, T) {
        n = read();
        sol();
    }
    return 0;
}

C. Longest Simple Cycle(dp)

oj: CodeForces

题意

给你多干条链,每条链有三个属性a,b,c。

c表示这条链有c个点。

a表示这条链的第1个点和上一条链的第a个点连在一起。

b表示这条链的最后一个点和上一条链的第b个点连在一起。

如图所示:

求出图中的最长简单环。

如图:

题解

定义dp[i]为第i条链能从前面获得的最大贡献(包含第i条链连向上一条链的两个边,不包含自身的长度)。

当我们考虑第i条链能形成的最长的环时,第i条链本身以及连上上一条链的两个边一定会被计算在内。剩下的有两种情况,其一是计算a和b之间的长度形成闭环。所以有:

dp[i] = 2 + abs(b[i] - a[i]);

其二是通过a和b两端进入更前面的链形成闭环。不过需要注意的是当a=b时,我们无法延伸到更前面的链,当i为1时,前面也没有链可供延伸。所以有:

if(a[i] != b[i] && i > 1)
    dp[i] = max(dp[i], 2 + dp[i - 1] + a[i] - 1 + c[i - 1] - b[i] - max(0, a[i] - b[i]) * 2);

这两种情况对后续的连法无任何影响,所以我们取最大值即可。

代码

#include <bits/stdc++.h>
#define _for(i, a) for(int i = 0, lennn = (a); i < lennn; ++i)
#define max(a, b) ((a) > (b) ? (a) : (b))
using namespace std;
typedef long long LL;
const int maxn = 100005;

inline int read() {
    int x(0), f(1); char ch(getchar());
    while (ch<'0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

int n;
LL a[maxn], b[maxn], c[maxn];
LL dp[maxn];

void init() {
    _for(i, n) dp[i] = 0;
}

void sol() {
    init();
    _for(i, n) c[i] = read();
    _for(i, n) a[i] = read();
    _for(i, n) b[i] = read();
    LL ans = 0;
    for(int i = 1; i < n; ++i) {
        dp[i] = 2 + abs(b[i] - a[i]);
        if(a[i] != b[i] && i > 1) dp[i] = max(dp[i], 2 + dp[i - 1] + a[i] - 1 + c[i - 1] - b[i] - max(0, a[i] - b[i]) * 2);
        ans = max(ans, c[i] - 1 + dp[i]);
    }
    printf("%lld\n", ans);
}

int main() {
    int T = read();
    _for(i, T) {
        n = read();
        sol();
    }
    return 0;
}

B. Inflation

oj: CodeForces

题意

有一件商品每个月都会涨价,初始价格是 p 0 p_0 p0 ,之后每个月都会涨 p i p_i pi

每次涨价的通货膨胀系数是 p i ∑ j = 0 i − 1 p j × 100 \frac{p_i}{\sum_{j=0}^{i-1}{p_j}}\times 100 j=0i1pjpi×100

你需要通过增加一些 p i p_i pi 使得每次涨价的通货膨胀系数都不超过 k k k

题解

考虑到后面的涨价对前面的没有影响,前面的涨价对后面的有影响,所以我们从后往前枚举。

∑ j = 0 i − 1 p j \sum_{j=0}^{i-1}{p_j} j=0i1pj 统称为 s u m sum sum 。由于影响 p i p_i pi 的因素只有 s u m sum sum ,而且我们只关心 s u m sum sum 的大小,而不关心具体的分配方式,所以我们利用 p i p_i pi 求出所需的最小的 s u m sum sum ,在和实际有的前缀和做差,这个差值就是我们需要在前面补上的最小的涨价数(差值为 0 0 0 或小于 0 0 0 就不补)。

针对每一个 p i p_i pi 都求出一个差值,所有的差值取最大值就是答案。

代码

#include <bits/stdc++.h>
#define _for(i, a) for(int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(int i = (a), lennn = (b); i <= lennn; ++i)
#define max(a, b) ((a) > (b) ? (a) : (b))
using namespace std;
typedef long long LL;
const int maxn = 1005;

inline int read() {
    int x(0), f(1); char ch(getchar());
    while (ch<'0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

int n, k;
LL a[maxn], d[maxn];

void sol() {
    _for(i, n) a[i] = read();
    d[0] = a[0];
    for(int i = 1; i < n; ++i) d[i] = d[i - 1] + a[i];
    LL ans = 0;
    for(int i = 1; i < n; ++i) {
        ans = max(ans, (a[i] * 100 + k - 1) / k - d[i - 1]);
    }
    printf("%lld\n", ans);
}

int main() {
    int T = read();
    _for(i, T) {
        n = read(), k = read();
        sol();
    }
    return 0;
}

A. K-divisible Sum

oj: CodeForces

题意

把若干个 k k k 分成 n n n 份,每份都不为 0 0 0 ,求出最小的最大块。

题解

先求出能保证每份都不为 0 0 0 的最小的 k k k 的倍数作为 k k k ,然后判断 k k k 能否被 n n n 整除即可。

代码

#include <bits/stdc++.h>
#define _for(i, a) for(int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(int i = (a), lennn = (b); i <= lennn; ++i)
using namespace std;
typedef long long LL;

inline int read() {
    int x(0), f(1); char ch(getchar());
    while (ch<'0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

int main() {
    int T = read();
    _for(i, T) {
        LL n = read(), k = read(), t = k;
        if(k % n) k = (n + k - 1) / k * k;
        if(k % n == 0) printf("%d\n", k / n);
        else printf("%d\n", k / n + 1);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值