Codeforces Round 924 (Div. 2)

Codeforces Round 924 (Div. 2)

A - Rectangle Cutting

解题思路:

初始矩形长宽为\((a,b)\),如果我们切\(a\),那么一定不能再拼接\(a\),否则一定一样。所以我们拼接\(b\),即将\(a\)对半分开得到两个\((\frac{a}{2},b)\)矩形拼接。

此时,如果\(\frac{a}{2} = b\)那么拼接出来的矩形和初始一样。

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;

void solve()
{
    int a, b;
    cin >> a >> b;
    if (a % 2 == 0 || b % 2 == 0)
    {
        if (a % 2 == 0)
        {
            if (b != a / 2)
            {
                puts("YES");
                return;
            }
        }
        if (b % 2 == 0)
        {
            if (a != b / 2)
            {
                puts("YES");
                return;
            }
        }
        puts("NO");
    }
    else
    {
        puts("NO");
    }
}

int main()
{
    int t = 1;
    cin >> t;
    while (t--)
    {
        solve();
    }

    return 0;
}

B - Equalize

解题思路:

排序,如果一段非递减区间中最大值减最小值的差在\(n -1\)之内,那么区间中不同数的个数就是通过加一个排列能得到相同数的数量。

所以一个队列扫过去。

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;

void solve()
{
    int n;
    cin >> n;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }
    sort(a.begin() + 1, a.end());
    int ans = 0;
    set<int> s;
    deque<int> q;
    for (int i = 1; i <= n; i++)
    {
        while (q.size() && a[i] - a[q.front()] >= n)
        {
            s.erase(a[q.front()]);
            q.pop_front();
        }
        q.push_back(i);
        s.insert(a[i]);
        ans = max(ans, (int)s.size());
    }
    cout << ans << endl;
}

int main()
{
    int t = 1;
    cin >> t;
    while (t--)
    {
        solve();
    }

    return 0;
}

C - Physical Education Lesson

解题思路:

对于当前\(x\)有两种位置情况,一种是从\(1 \sim k\)时数到\(x\),另一种是\(k - 1 \sim 2\)时数到\(x\)

  • 第一种:\(2k - 2= n - x = s\)
  • 第二种:\(2k - 2 = n + (x - 2) = s\)

我们对两种\(s\)分别枚举出其因子,然后求出\(k\)的个数即可。

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;

void solve()
{
    int n, x;
    cin >> n >> x;
    int s = n - x;
    vector<int> f;
    for (int i = 2; i <= s / i; i++)
    {
        if (s % i == 0)
        {
            f.push_back(i);
            if (s / i != i)
            {
                f.push_back(s / i);
            }
        }
    }
    f.push_back(s);
    set<int> se;
    for (auto v : f)
    {
        int t = (v + 2) / 2;
        if (t != 1 && t >= x && t * 2 - 2 == v)
        {
            se.insert(t);
        }
    }
    if (x > 1)
    {
        s = n + max(0, x - 2);
        f.clear();
        for (int i = 2; i <= s / i; i++)
        {
            if (s % i == 0)
            {
                f.push_back(i);
                if (s / i != i)
                {
                    f.push_back(s / i);
                }
            }
        }
        f.push_back(s);
        for (auto v : f)
        {
            int t = (v + 2) / 2;
            if (t > 1 && t >= x && t * 2 - 2 == v)
            {
                se.insert(t);
            }
        }
    }
    cout << se.size() << endl;
}

int main()
{
    int t = 1;
    cin >> t;
    while (t--)
    {
        solve();
    }

    return 0;
}

D - Lonely Mountain Dungeons

解题思路:

首先,根据\(\sum c_i \leq 2 \times 10^5\),可以发现,我们可以枚举队伍的数量。最多有\(\sqrt{n}\)种不同的种族人数。

对于任意队伍数量\(i\),我们发现,每个种族的人尽量平均地分到每个队伍中是最优的。

手算规律可以发现,对于每个种族人数\(v\),给定要划分的队伍数\(i\),平均划分得到的最大对数是可以\(O(1)\)计算得到的。

时间复杂度\(O(n log(2\times10^5))\)。种族数量的根号均摊\(+\) \(map\)\(log\)查找,时间复杂度不一定对,但大概不会超过这个\(O(n \sqrt{n})\)

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;
ll qmi(ll a, ll b)
{
    ll res = 1;
    while (b)
    {
        if (b & 1)
        {
            res = res * a;
        }
        a = a * a;
        b >>= 1;
    }
    return res;
}
void solve()
{
    ll n, b, x;
    cin >> n >> b >> x;
    vector<ll> a(n + 1);
    ll ma = 0;
    map<ll, ll> cnt;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        ma = max(a[i], ma);
        cnt[a[i]]++;
    }
    map<ll, ll> cost;
    ll ans = 0;
    for (int i = 1; i <= ma; i++)
    {
        ll sum = 0;
        for (auto t : cnt)
        {
            ll x = t.fi / i;
            ll y = t.fi % i;
            ll res = (i - y) * (i - y - 1) / 2 * x * x;
            if (y > 0)
            {
                res += y * (i - y) * x * (x + 1);
            }
            if (y > 1)
            {
                res += (y * (y - 1)) / 2 * (x + 1) * (x + 1);
            }
            if (res > cost[t.fi])
            {
                cost[t.fi] = res;
            }
            sum += t.se * cost[t.fi] * b;
        }
        sum -= (i - 1) * x;
        ans = max(ans, sum);
    }
    cout << ans << endl;
}

int main()
{
    int t = 1;
    cin >> t;
    while (t--)
    {
        solve();
    }

    return 0;
}

E - Modular Sequence

解题思路:

  • \({a_i} \mod {y} = r\)
  • \(a_i = p_iy + r\),\((\sum\limits_{i = 1}^np_i)\times y + nr = S\)
  • \(p_{i + 1} = p_i + 1\)或者\(0\)

\(p1 = x \mod y\)。所以对应\(p\)序列应当是:

\(((p1,p1 + 1,p1 + 2,...,p1 + k_1),(0,1,2,..,k_2),(0,1,2,...,k_3),...,(0,0,0,0,0,0,0))\)

除去第一个是从\(p1\)开始上升,后面都是先成\(0\)然后上升。最后如果前面的和已经足够,后续补零即可。

\(s = \frac{S - nr}{y}\)\(s = \sum\limits_{i = 1}^np_i\)

\(dp[i] : 构造出p之和为i需要的最小元素数量\).

由于\((0,1,2,...,k)\)明显是高斯和,且\(s \leq 2\times 10^5\).所以,一段小序列最长为\(\sqrt{2\times 10^5}\)

\(dp\)时间复杂度为\(O(\sqrt{n})\)

\(p_1\)所处小段枚举构造\(O(n)\)

后面的小段根据\(dp\)反向构造\(O(n\sqrt{n})\)

最后还有空间就补零。

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;
const int N = 2e5 + 10;
int dp[N];
int ans[N];
void solve()
{
    ll n, x, y, s;
    cin >> n >> x >> y >> s;
    ans[1] = x;
    ll r = x % y;
    s -= n * r;
    if (s % y || s < 0)
    {
        puts("No");
        return;
    }
    s /= y;
    int p1 = x / y;
    int sum = p1;
    int pos = -1;
    for (int i = 1; i <= n && s >= sum; i++)
    {
        // 接上一个新序列能构造成功
        if (dp[s - sum] <= n - i)
        {
            pos = i;
            break;
        }
        p1++;
        sum += p1;
    }
    if (pos == -1)
    {
        puts("No");
        return;
    }
    for (int i = 2; i <= pos; i++)
    {
        ans[i] = ans[i - 1] + y;
    }
    s -= sum;
    int last = dp[s];
    // 构造完全背包的解
    while (s)
    {
        int k = 1;
        while (true)
        {
            int t = k * (k + 1) / 2;
            if (dp[s - t] + k + 1 == last)
            {
                last = dp[s - t];
                s -= t;
                for (int i = 0; i < k + 1; i++)
                {
                    ans[++pos] = i * y + r;
                }
                break;
            }
            k++;
        }
    }
    // 剩余位置补零
    for (int i = pos + 1; i <= n; i++)
    {
        ans[i] = r;
    }
    puts("Yes");
    for (int i = 1; i <= n; i++)
    {
        cout << ans[i] << ' ';
    }
    cout << endl;
}

int main()
{
    int k = 1;
    int m = 2e5;
    for (int i = 1; i <= m; i++)
    {
        dp[i] = 1e9 + 1;
    }
    dp[0] = 0;
    while (true)
    {
        int t = k * (k + 1) / 2;
        if (t > m)
        {
            break;
        }
        for (int i = t; i <= m; i++)
        {
            dp[i] = min(dp[i], dp[i - t] + k + 1);
        }
        k++;
    }
    int t = 1;
    cin >> t;
    while (t--)
    {
        solve();
    }

    return 0;
}
posted @ 2024-02-11 19:51  value0  阅读(373)  评论(0)    收藏  举报