2023-05 多校联合训练 ZJNU站 正式赛

Scarlett的三元组

有一个长度为 \(n\) 的序列 \(a_1,a_2,\cdots,a_n\),试问有多少个三元组 \((a_i,a_j,a_k)\) 满足:

  • \(1 \le i \lt j \lt k \le n\)
  • \(a_i + a_k \le a_j\)
  • \((2\le n \le 2000)\)

题解:排序 + 尺取 \(O(n^2logn)\)

  • 我们考虑\(3\)个位置中最特殊的位置\(j\)
  • 对其左右两侧进行排序,左侧降序,右侧升序 \(O(nlogn)\)
  • 然后考虑到单调性,我们利用尺取法计算答案 \(O(n)\)
#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<double, double> pdd;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 2e3 + 10, M = 4e5 + 10;

int n;
int a[N];
int b[N];

void solve()
{
    cin >> n;
    for (int i = 1; i <= n; ++i)
        cin >> a[i];
    int ans = 0;
    for (int i = 1; i <= n; ++i)
    {
        for (int i = 1; i <= n; ++i)
            b[i] = a[i];
        sort(b + 1, b + i, greater<int>());
        sort(b + i + 1, b + n + 1);
        bool ishead = true;
        for (int j = i - 1, k = i + 1; j >= 1; j--)
        {
            while (k <= n && b[j] + b[k] <= a[i] && ishead)
            {
                k++;
                ans++;
            }
            if (ishead)
            {
                ishead = false;
                k--;
                continue;
            }
            while (k > i && b[j] + b[k] > a[i])
                k--;
            ans += k - i;
        }
    }
    cout << ans << endl;
}
signed main(void)
{
    Zeoy;
    int T = 1;
    // cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}

韩教练发金币

韩教练有 \(n\) 枚金币,每一枚金币上都印有一个整数 \(a_i\),代表这枚金币的价值。教练现在想要给 Scarlett 、wxy977 、illume 发放金币。

这三个人都各自有着不同的想法。wxy977 希望每个人发到的金币数量相同,illume 希望每个人发到的金币的价值总和不同,而 Scarlett 希望所有的金币都被发完

韩教练希望能同时满足三个人的想法

\((1 \le n \le 10^5)\)

如果无论怎么分配金币,都不能同时满足三人的想法,请在一行内输出 No

否则,请在第一行输出 Yes,并在接下来三行内的每一行输出 \(\frac n 3\) 个整数,每个数代表一个人所发到的金币的价值

如果答案不唯一,你只需要输出任意一组满足条件的解即可。

题解:构造

  • 如果\(n\)不是\(3\)的倍数一定无解
  • 如果金币的种类只有\(1\)种也一定无解
  • 我们不妨考虑一个这样的构造:
  • 现将所有金币排序,令\(m=\frac n 3\)
  • 如果排序后\(a_1=a_{2m}\),说明第一行和第二行金币一样,我们不妨从第\(3\)行中找到一个\(x\neq a_1\)\(a_1\)进行交换,并且需要保证交换后第三行和第一行的金币总额不同,如果相同说明无解
  • 如果排序后\(a_{m+1}=a_{3m}\),按照上述构造方法构造即可
#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<double, double> pdd;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 2e5 + 10, M = 4e5 + 10;

int n;
int a[N];
map<int, int> mp;

void solve()
{
    cin >> n;
    for (int i = 1; i <= n; ++i)
    {
        cin >> a[i];
        mp[a[i]]++;
    }
    sort(a + 1, a + n + 1);
    if (n % 3 != 0 || mp.size() == 1)
    {
        cout << "No" << endl;
        return;
    }
    int m = n / 3;
    int pre0 = 0, pre1 = 0, pre2 = 0;
    for (int i = 1; i <= m; ++i)
        pre0 += a[i];
    for (int i = m + 1; i <= 2 * m; ++i)
        pre1 += a[i];
    for (int i = 2 * m + 1; i <= 3 * m; ++i)
        pre2 += a[i];
    if (a[1] == a[2 * m])
    {
        bool flag = false;
        for (int i = 2 * m + 1; i <= 3 * m; ++i)
        {
            if (a[i] != a[1] && pre2 - a[i] + a[1] > pre0 + a[i] - a[1])
            {
                swap(a[i], a[1]);
                flag = true;
                break;
            }
        }
        if (!flag)
        {
            cout << "No" << endl;
            return;
        }
    }
    else if (a[m + 1] == a[3 * m])
    {
        bool flag = false;
        for (int i = 1; i <= m; ++i)
        {
            if (a[i] != a[m + 1] && pre1 + a[i] - a[m + 1] > pre0 + a[m + 1] - a[i])
            {
                swap(a[i], a[m + 1]);
                flag = true;
                break;
            }
        }
        if (!flag)
        {
            cout << "No" << endl;
            return;
        }
    }
    cout << "Yes" << endl;
    for (int i = 1; i <= m; ++i)
        cout << a[i] << "\n "[i < m];
    for (int i = m + 1; i <= 2 * m; ++i)
        cout << a[i] << "\n "[i < 2 * m];
    for (int i = 2 * m + 1; i <= 3 * m; ++i)
        cout << a[i] << "\n "[i < 3 * m];
}
signed main(void)
{
    Zeoy;
    int T = 1;
    // cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}

Beautiful Arrays

Jerry Black 有一个长度为 \(N\) 的序列 \(\{1,2,\cdots,N\}\)\(M\) 个区间 \([l_1,r_1],[l_2,r_2],\cdots,[l_M,r_M]\).

现在他可以选择一个正整数 \(L\) \((L \ge K)\) 然后将序列划分成 \([1,L],[L+1,2L],\cdots,[tL+1,n]\)\(t+1\) 段,其中 \(t\) 是满足 \(tL+1 \le N\) 的最大非负整数。

定义一个序列为好序列,当且仅当存在至少一种合法的分法 \([L_1,R_1],[L_2,R_2],\cdots,[L_S,R_S]\),并对于任意的 \(i \in [1,S]\),都存在 \(j \in [1,M]\),使得 \(l_j \le L_i \le R_i \le r_j\)。换言之,任何一段被划分出来的段都应被完全包含至少一个区间内。

Jerry Black 想知道他的这个序列是否是一个好序列

\(N,M,K\) \((1 \le K \le N \le 2 \times 10^5,\ 1 \le M \le 2 \times 10^5)\)

题解:调和级数 + 线段树 \(O(nlog^2n)\)

  • 容易发现如果我们枚举\(L\)\(K\)\(N\),发现区间总个数为调和级数\(nlogn\)
  • 那么我们只需要利用线段树维护最大值即可,支持单点修改和区间查询
  • 对于\(M\)个区间的左端点\(l_i\),我们单点修改,在其\(l_i\)处加上\(r_i\)
  • 然后对于枚举\(L\)的每个区间\([l,r]\),我们只要查询\([1,l]\)中的最大值\(mx\),如果\(mx < r\),那么该分法一定不合法
#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<double, double> pdd;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 2e5 + 10, M = 4e5 + 10;

int n, m, k;
struct info
{
    int mx;
};
struct node
{
    info val;
} seg[N << 2];

info operator+(const info &a, const info &b)
{
    info c;
    c.mx = max(a.mx, b.mx);
    return c;
}

void up(int id)
{
    seg[id].val = seg[lson].val + seg[rson].val;
}

void change(int id, int l, int r, int x, int val)
{
    if (l == r)
    {
        seg[id].val.mx = max(seg[id].val.mx, val);
        return;
    }
    int mid = l + r >> 1;
    if (x <= mid)
        change(lson, l, mid, x, val);
    else
        change(rson, mid + 1, r, x, val);
    up(id);
}

info query(int id, int l, int r, int ql, int qr)
{
    if (ql <= l && r <= qr)
        return seg[id].val;
    int mid = l + r >> 1;
    if (qr <= mid)
        return query(lson, l, mid, ql, qr);
    else if (ql > mid)
        return query(rson, mid + 1, r, ql, qr);
    else
        return query(lson, l, mid, ql, qr) + query(rson, mid + 1, r, ql, qr);
}

void solve()
{
    cin >> n >> m >> k;
    for (int i = 1; i <= m; ++i)
    {
        int l, r;
        cin >> l >> r;
        change(1, 1, n, l, r);
    }
    for (int i = k; i <= n; ++i)
    {
        bool flag = true;
        for (int j = 1; j <= n; j += i)
        {
            int l = j, r = j + i - 1;
            if (r > n)
                r = n;
            if (query(1, 1, n, 1, l).mx < r)
            {
                flag = false;
                break;
            }
        }
        if (flag)
        {
            cout << "Yes" << endl;
            return;
        }
    }
    cout << "No" << endl;
}
signed main(void)
{
    Zeoy;
    int T = 1;
    // cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}

时空风暴

JiangRH 迷失在了一个未知宇宙之中。

该宇宙内共有 \(n\) 个星球,分别编号为 \(1,2,3,\cdots,n\).

JiangRH 目前位于编号为 \(s\) 的星球。

已知在编号为 \(t\) 的星球上有一个逃离该未知宇宙的设备,只要 JiangRH 到达该星球,他就可以马上逃离这个宇宙。

JiangRH 希望可以尽快离开这该死的未知宇宙。

逃离的路途通常是困难的,并非任意两个星球之间都可以安全地直接通行。JiangRH 只知道若干条安全的路线,以及通过每一条路线所需要花费的时间。

在逃离的过程中,JiangRH 发现在不久的将来会发生多次时空风暴,且每次时空风暴出现的时间互不相同。每次遇到时空风暴,JiangRH 都会被传送到某个指定的星球上,除非在这次时空风暴发生之前他已经逃离了这个宇宙。当然,如果在某次时空风暴发生的同时 JiangRH 刚好到达编号为 \(t\) 的星球,我们认为他能够以极快的速度成功逃离,并不会受到该次时空风暴的影响。

请问 JiangRH 最早能在什么时候离开这个未知宇宙?

题解:反向\(dij\)

  • 因为不管怎么传送,我们都必须前往编号\(t\)的星球
  • 所以我们可以将编号\(t\)星球当作源点进行\(dij\)
  • 然后将时空风暴出现的时间进行排序后,对于每一个传送之间的时间间隔看能否到达编号\(t\)即可
  • 如果传送到了最后一个星球,但是最后星球到不了编号\(t\)的星球,说明一定无解
#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<double, double> pdd;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 2e5 + 10, M = 4e5 + 10;

int n, m, s, t, k;
vector<pii> g[N];
int dis[N];
bool vis[N];
pii qry[N];

void dij(int st)
{
    for (int i = 1; i <= n; ++i)
        dis[i] = INF;
    priority_queue<pii, vector<pii>, greater<pii>> q;
    dis[st] = 0;
    q.push({dis[st], st});
    while (q.size())
    {
        int u = q.top().second;
        q.pop();
        if (vis[u])
            continue;
        for (auto [v, w] : g[u])
        {
            if (dis[v] > dis[u] + w)
            {
                dis[v] = dis[u] + w;
                q.push({dis[v], v});
            }
        }
    }
}

void solve()
{
    cin >> n >> m >> s >> t;
    for (int i = 1, u, v, w; i <= m; ++i)
    {
        cin >> u >> v >> w;
        if (u == v)
            continue;
        g[u].push_back({v, w});
        g[v].push_back({u, w});
    }
    dij(t);
    cin >> k;
    for (int i = 1; i <= k; ++i)
        cin >> qry[i].first >> qry[i].second;
    qry[++k] = {0, s};
    sort(qry + 1, qry + k + 1);
    int ans = 0;
    for (int i = 1; i < k; ++i)
    {
        int ed = qry[i].second;
        int d = qry[i + 1].first - qry[i].first;
        if (dis[ed] <= d)
        {
            ans += dis[ed];
            cout << ans << endl;
            return;
        }
        ans += d;
    }
    if (dis[qry[k].second] != INF)
    {
        ans += dis[qry[k].second];
        cout << ans << endl;
        return;
    }
    cout << "JiangRH is not needed anymore" << endl;
}
signed main(void)
{
    Zeoy;
    int T = 1;
    // cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}

有个大坏蛋但我不说是谁

一年一度的小军训来咯!某班共 \(n\) 名同学正在树荫下坐成一排休息,位置从左到右分别编号为 \(1,2,\cdots,n\),你坐在第 \(p\) 个位置。

班主任发现这群小宝贝们都快被太阳晒成干了,于是赶忙去买了 \(m\) 个单位的水,让你给大家分发下去。而你,作为这个班的班长,却在想着如何能让自己分到更多的水!(太坏啦!)

因为大家正坐成一排,我们假设每个人只能看到自己以及与自己相邻的同学分到了多少个单位的水。只要相邻的同学之间分配到的水的差值不超过 \(d\) 个单位,大家是不会抱怨班长分配不均的。

已知每位同学至少应当分配到 \(1\) 个单位的水,请问在大家都不会抱怨分配不均的前提下,你最多能够分给自己多少个单位的水?

题解:二分答案 + 模拟

  • 二分答案即可
  • \(check\)时模拟过程并利用等差数列求和即可
#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<double, double> pdd;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 2e5 + 10, M = 4e5 + 10;

int n, m, d, p;

bool check(int mid)
{
    int res = 0;
    int k = mid / d;
    if (mid % d == 0)
        k--;
    k = max(1LL, p - k);
    res += ((p - k + 1) * (mid - (p - k) * d + mid)) / 2;
    if (k > 1)
        res += k - 1;
    k = mid / d;
    if (mid % d == 0)
        k--;
    k = min(n, p + k);
    res += ((k - p + 1) * (mid - (k - p) * d + mid)) / 2;
    if (k < n)
        res += n - k;
    res -= mid;
    if (res <= m)
        return true;
    else
        return false;
}

void solve()
{
    cin >> n >> m >> d >> p;
    int l = 1, r = m;
    while (l <= r)
    {
        int mid = l + r >> 1;
        if (check(mid))
            l = mid + 1;
        else
            r = mid - 1;
    }
    cout << r << endl;
}
signed main(void)
{
    Zeoy;
    int T = 1;
    cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}

千万玩家的枪战梦想

知名线下真人枪战赛事 Counter Fire World Final 目前正在下北泽举行。

所有参赛选手会在一张无限大小的网格图内进行比赛,但初始时的位置会落在一个大小为 \(n \times m\) 的矩形区域内,并且每个位置一开始最多只会有一位选手存在。

赛内共设置了 \(26\) 个阵营,以 \(\text{A},\text{B},\cdots,\text{Z}\) 进行标识。每位选手在比赛开始前会被随机分配到一个阵营中。

作为本场赛事的主办方,你需要在比赛开始时对场上局势进行解说。你打算按照以下方案判断局势走向:

  • 对每位选手单独进行判断,并假定除了该选手以外,其它选手所处的位置并不会发生变化;
  • 当前选手每次只能够选择走到相邻的四个网格(上、下、左、右)中的任意一个;
  • 如果下一个网格为空位置,或者存在的选手归属于己方阵营,那么当前选手便可以走到该网格内;
  • 如果当前选手被非己方阵营的其它选手所包围(即能够行走的网格总数有限),便认定该选手一定会在未来的某一时刻被淘汰,否则认为该选手有可能存活到最后。

试判断在所有出现过的阵营中,有哪些阵营到最后可能还会有至少一名选手未被淘汰,又有哪些阵营会被完全淘汰?

\((3 \le n,m \le 1000)\)

题解:BFS/DFS \(O(26nm)\)

\(26\)个字母分别进行\(bfs\)即可

#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<double, double> pdd;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 1e3 + 10, M = 4e5 + 10;

int n, m;
char g[N][N];
bool vis[N][N];
unordered_map<char, bool> mp;
int dir[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};

struct node
{
    int x, y;
};

bool bfs(char type)
{

    queue<node> q;
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j)
        {
            if (g[i][j] == type)
            {
                q.push({i, j});
                vis[i][j] = true;
            }
            else
                vis[i][j] = false;
        }
    while (q.size())
    {
        node u = q.front();
        q.pop();
        if (u.x == 1 || u.x == n || u.y == 1 || u.y == m)
            return true;
        for (int i = 0; i < 4; ++i)
        {
            int nx = u.x + dir[i][0];
            int ny = u.y + dir[i][1];
            if (nx >= 1 && nx <= n && ny >= 1 && ny <= m && !vis[nx][ny] && (g[nx][ny] == '.' || g[nx][ny] == type))
            {
                vis[nx][ny] = 1;
                q.push({nx, ny});
            }
        }
    }
    return false;
}

void solve()
{
    cin >> n >> m;
    mp.clear();
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j)
        {
            cin >> g[i][j];
            mp[g[i][j]] = true;
        }
    vector<char> win, lose;
    for (int i = 0; i < 26; ++i)
    {
        if (!mp[(char)('A' + i)])
            continue;
        if (bfs((char)('A' + i)))
            win.push_back((char)('A' + i));
        else
            lose.push_back((char)('A' + i));
    }
    if (win.size())
    {
        for (auto ch : win)
            cout << ch;
    }
    else
        cout << "-";
    cout << " / ";
    if (lose.size())
    {
        for (auto ch : lose)
            cout << ch;
    }
    else
        cout << "-";
    cout << endl;
}
signed main(void)
{
    Zeoy;
    int T = 1;
    cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}
posted @ 2023-05-24 19:50  Zeoy_kkk  阅读(14)  评论(0编辑  收藏  举报