Educational Codeforces Round 92 (Rated for Div. 2)

A. LCM Problem

水题,显然 2 × a ≤ l c m ( a , b )    ,    b ≤ l c m ( a , b ) 2\times a\le \mathrm{lcm}(a,b) \; , \;b\le \mathrm{lcm}(a,b) 2×alcm(a,b),blcm(a,b),那么就做完了。

B. Array Walk

很明显是个DP,但是我调了好久才对,还是太菜了。
推导过程如下:
首先对于 z = 0 z=0 z=0 的情况,显然最终结果是 d p 0 = m a x 0 = ∑ i = 1 k + 1 a i dp_0=max_0=\sum_{i=1}^{k+1}a_i dp0=max0=i=1k+1ai
所以可以建一个前缀和数组。

z = 1 z=1 z=1的时候除了不仅可以像上面那样走,还可以选择在路程中间后退一次或者在末端后退一次。在中间后退使得路径长度减少2,中间有连续的两个块会被经过两次;在末端后退会路径长度减少1,路径的倒数第二个块经过两次。于是可以再开一个数组记录到当前位置为止的最大相邻块值的和,即 m [ i ] = max ⁡ ( m [ i − 1 ] , a [ i − 1 ] + a [ i ] ) m[i] = \max(m[i - 1], a[i - 1] + a[i]) m[i]=max(m[i1],a[i1]+a[i])
那么 m a x 1 = max ⁡ ( p r e [ k − 1 ] + m [ k − 1 ] , p r e [ k ] + a [ k − 1 ] ) max_1=\max(pre[k-1] + m[k-1], pre[k] + a[k-1]) max1=max(pre[k1]+m[k1],pre[k]+a[k1])
显然, d p 1 = max ⁡ ( d p 0 , m a x 1 ) dp_1=\max(dp_0,max_1) dp1=max(dp0,max1)

z ≥ 2 z\ge2 z2 的时候以此类推,可以得到 d p i = max ⁡ ( d p i − 1 , m a x i ) dp_i=\max(dp_{i-1},max_i) dpi=max(dpi1,maxi) 以及
m a x i = max ⁡ ( p r e [ k + 1 − 2 × i ] + i × m [ k + 1 − 2 × i ] , p r e [ k + 2 − 2 × i ] + ( i − 1 ) × m [ k + 2 − 2 × i ] + a [ k + 1 − 2 × i ] ) max_i=\max(pre[k + 1 - 2 \times i] + i \times m[k + 1 - 2 \times i], pre[k + 2 - 2 \times i] + (i - 1) \times m[k + 2 - 2 \times i] + a[k + 1 - 2 \times i]) maxi=max(pre[k+12×i]+i×m[k+12×i],pre[k+22×i]+(i1)×m[k+22×i]+a[k+12×i])

于是这题就做完了,复杂度 O ( k ) O(k) O(k)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef vector<int> vi;
const int INF = 0x3f3f3f3f;
const int maxn = 1e5 + 9;
inline int read()
{
    int data = 0, f = 1;
    char ch = getchar();
    while (!isdigit(ch))
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (isdigit(ch))
    {
        data = (data << 3) + (data << 1) + ch - '0';
        ch = getchar();
    }
    return f * data;
}
int arr[maxn], pre[maxn], m[maxn];

int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("data.txt","w",stdout);
    //std::ios::sync_with_stdio(false);
    //std::cin.tie(0);
    int T;
    T = read();
    while (T--)
    {
        memset(m, 0, sizeof(m));
        int n = read(), k = read(), z = read();
        for (int i = 1; i <= n; ++i)
        {
            arr[i] = read();
            pre[i] = pre[i - 1] + arr[i];
            m[i] = max(m[i - 1], arr[i - 1] + arr[i]);
        }
        int ans = pre[k + 1], tmp;
        for (int i = 1; i <= z; ++i)
        {
            if (k < 2 * i - 1)
                break;
            tmp = max(pre[k + 1 - 2 * i] + i * m[k + 1 - 2 * i], pre[k + 2 - 2 * i] + (i - 1) * m[k + 2 - 2 * i] + arr[k + 1 - 2 * i]);
            ans = max(ans, tmp);
        }
        cout << ans << '\n';
    }
    //fclose(stdin);
    //fclose(stdout);
    return 0;
}

C. Good String

比赛的时候理解错了题意,试了二分和尺取法结果都过不了样例。第二天再看发现题目读错了。。。。。
显然,满足题目要求的左移和右移后相同的序列只可能是两种:

  1. 所有的元素全部相同
  2. 两个不同元素交替循环,如 0101010101

可以发现,如果序列长度是奇数那么必须是第一种
由于所有字符都是数字,可以把所有组合都枚举一遍计算好串长度最大值,再用总长度减一下就行了
这题到这里也就做完了

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef vector<int> vi;
const int INF = 0x3f3f3f3f;
const int maxn = 1e5 + 9;
template <typename T>
inline void read(T &x)
{
    T data = 0, f = 1;
    char ch = getchar();
    while (!isdigit(ch))
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (isdigit(ch))
    {
        data = (data << 3) + (data << 1) + ch - '0';
        ch = getchar();
    }
    x = f * data;
}
string s;
int calc(int x, int y)
{
    int ans = 0;
    for (int i = 0; i < s.length(); ++i)
        if (s[i] - '0' == x)
        {
            ans++;
            swap(x, y);
        }
    if (x != y && ans & 1)
        ans--;
    return ans;
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("data.txt","w",stdout);
    //std::ios::sync_with_stdio(false);
    //std::cin.tie(0);
    int T;
    read(T);
    while (T--)
    {
        cin >> s;
        int ans = 0;
        for (int i = 0; i <= 9; ++i)
            for (int j = 0; j <= 9; ++j)
                ans = max(ans, calc(i, j));
        cout << s.length() - ans << '\n';
    }
    //fclose(stdin);
    //fclose(stdout);
    return 0;
}

D. Segment Intersections

又是一个题面令人费解的题,并且比C难多了。
分类讨论很复杂,尤其两条线段不相交的情况。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template <typename T>
inline void read(T &x)
{
    T data = 0, f = 1;
    char ch = getchar();
    while (!isdigit(ch))
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (isdigit(ch))
    {
        data = (data << 3) + (data << 1) + ch - '0';
        ch = getchar();
    }
    x = f * data;
}
ll n, k, l1, l2, r1, r2;
int main()
{
    int t;
    read(t);
    while (t--)
    {
        read(n), read(k), read(l1), read(r1), read(l2), read(r2);
        ll ans = 1e18;
        if (max(l1, l2) <= min(r1, r2))
        {
            ll rem = max((ll)0, k - n * (min(r1, r2) - max(l1, l2)));
            ll mp = n * (abs(l1 - l2) + abs(r1 - r2));
            ans = min(rem, mp) + max((ll)0, rem - mp) * 2;
        }
        else//不相交
        {
            ll inv = max(l1, l2) - min(r1, r2);//间隔
            for (int i = 1; i <= n; ++i)
            {
                ll tmp = i * inv;//补上间隔
                ll mp = (max(r1, r2) - min(l1, l2)) * i;//最大长度
                tmp += min(k, mp) + max((ll)0, k - mp) * 2;//调整
                ans = min(ans, tmp);
            }
        }
        cout << ans << '\n';
    }
}

照着题解的kotlin代码写了一份c++的

E. Calendar Ambiguity

由题, x × d + y ≡ y × d + x ( m o d w ) x\times d+y\equiv y\times d+x \pmod w x×d+yy×d+x(modw),变形可得:
( x − y ) × ( d − 1 ) ≡ 0 ( m o d w ) (x-y)\times(d-1)\equiv 0\pmod w (xy)×(d1)0(modw)
那么就很明显了,既然 d d d w w w 都已知了, x − y x-y xy 一定是 w gcd ⁡ ( w , d − 1 ) \frac{w}{\gcd(w,d-1)} gcd(w,d1)w 的整数倍。接下来等差数列求和就可以解决了。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template <typename T>
inline void read(T &x)
{
    T data = 0, f = 1;
    char ch = getchar();
    while (!isdigit(ch))
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (isdigit(ch))
    {
        data = (data << 3) + (data << 1) + ch - '0';
        ch = getchar();
    }
    x = f * data;
}
ll m, d, w;
int main()
{
    int t;
    read(t);
    while (t--)
    {
        read(m), read(d), read(w);
        ll w2 = w / __gcd(d - 1, w);
        ll md = min(m, d);
        ll cnt = md / w2;
        ll ans = (2 * md - w2 * (cnt + 1)) * cnt / 2;
        cout << ans << '\n';
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值