The 15th Shandong CCPC Provincial Collegiate Programming Contest(补题

赛时4题,银尾

L. Stella

知识点:签到

思路:模拟即可

D. Distributed System

知识点:差分

思路:对于覆盖全部的,记录一个懒标记,对于部分的那些,会有两部分,\(b_i\)\(n-1\),和 \(0\)\(b_i+n-1\),分别取模,需要处理两次,也可以不处理两次,开 \(2*n\) 的差分数组,处理一次,最后输出时 输出 \(a_i+a_{i+n}+sum\) 即可

Code
点击查看代码
void solve()
{
    int q;
    cin >> n >> q;
    vi a(2 * n + 7);
    int sum = 0;
    while (q--)
    {
        cin >> x >> y;
        sum += x / n;
        x %= n;
        a[y]++;
        a[y + x]--;
    }
    for (int i = 1; i < 2 * n; i++)
    {
        a[i] += a[i - 1];
    }
    for (int i = 0; i <= n - 1; i++)
    {
        cout << a[i] + a[i + n] + sum << ' ';
    }
    cout << endl;
}

G. Assembly Line

知识点:贪心

思路:如果假设无任何限制条件,处理出每个工件处理完成的时间,但是由于每个工人每分钟只能处理一个工件,所以对于同一时间处理的工件,时间就要向后移,对时间数组排序,然后递推 \(a_i=max(a_{i-1}+1,a_i)\),最后一个工件处理完成的时间就是最终结果

H. Minimum Spanning Tree

知识点:最小生成树,贪心

思路:先在原来的大树上进行最小生成树操作,注意到,我们如果在相邻点之间加边,这样的边权是最小的,所以我们去除最小生成树上边权大于 \(1\) 的那 \(k\) 个边,自己在相邻而不连通的点之间加边即可

Code
点击查看代码
int fa[M];
void init()
{
    for (int i = 1; i <= n; i++)
    {
        fa[i] = i;
    }
}
int find(int x)
{
    return fa[x] == x ? x : fa[x] = find(fa[x]);
}
bool same(int a, int b)
{
    return find(a) == find(b);
}
void join(int u, int v)
{
    u = find(u);
    v = find(v);
    if (u != v)
    {
        if (u > v)
        {
            fa[u] = v;
        }
        else
        {
            fa[v] = u;
        }
    }
}
struct node
{
    int u, v, w, id;
};
struct node2
{
    int u, v;
};

bool cmp(node a, node b)
{
    return a.w < b.w;
}
void solve()
{
    cin >> n >> m >> k;
    init();
    vector<node> a;
    for (int i = 0; i < m; i++)
    {
        int u, v, w;
        cin >> u >> v >> w;
        a.push_back({u, v, w, i + 1});
    }
    sort(all(a), cmp);
    vector<node> e2;
    int ans = 0;
    for (auto [u, v, w, id] : a)
    {
        if (!same(u, v))
        {
            join(u, v);
            e2.pb({u, v, w, id});
            ans += w;
        }
    }
    sort(all(e2), cmp);
    int op = 0;
    for (auto [u, v, w, id] : e2)
    {
        cerr << u << ' ' << v << endl;
    }
    for (int i = e2.size() - 1; i >= 0; i--)
    { // 去除边

        auto [u, v, w, id] = e2[i];
        if (w <= 1)
        {
            break;
        }
        // if (abs(u - v) >= w)
        // {

        // }
        if (op == k)
            break;
        op++;
        ans -= w;
        e2.pop_back();
        if (op == k)
            break;
    }
    for (auto [u, v, w, id] : e2)
    {
        cerr << u << ' ' << v << endl;
    }
    vector<node> b;
    vi res; // 最后的边编号
    init();
    for (int i = 0; i < e2.size(); i++) // 剩下的边
    {
        auto [u, v, w, id] = e2[i];
        res.push_back(id);
        join(u, v);
    }
    vector<node2> e; // 加的边
    int cnt = m;
    for (int i = 2; i <= n; i++)
    {
        if (!same(i, i - 1))
        {
            join(i, i - 1);
            cnt++;
            res.pb(cnt);
            e.pb({i, i - 1});
            ans++;
        }
    }
    cout << cnt - m << endl;
    for (auto [u, v] : e)
    {
        cout << u << ' ' << v << endl;
    }
    cout << ans << endl;
    for (auto i : res)
    {
        cout << i << ' ';
    }
    cout << endl;
}

A. Project Management

知识点:二分,贪心

思路:求最大选的人数,考虑二分,
先考虑排序规则,如果要在同等级中选,一定是先选包容性强的,如果要在不同等级中选,肯定是先选等级低的,因为如果选高等级的,对于低等级的限制不确定,所以选低等级的。
考虑 \(check\) 函数怎么写,二分人数,传进去人数,返回是否能达到(因为如果高人数可以满足,低人数一定可以满足,具有单调性)遍历所有等级,在一个等级中,如果发现当前选的高等级容纳量加上现在选的数量不满足二分的 \(mid\),这个就不能选,同时这个等级后面的所有人容纳性只会更低,所以都不能选,直接跳到下一个等级,一直进行这样的操作,判断最终人数是否满足答案,找出人数最大值,在知道最大值的情况下,带着这个数值再去找一遍就行了

Code
点击查看代码
struct node
{
    int u;
    int v;
    int id;
};
vector<node> a;

bool cmp(node a, node b)
{
    if (a.u == b.u)
        return a.v > b.v;
    return a.u < b.u;
}

int mx = 0;

bool check(int mid)
{
    int now = 0;
    for (int i = 1; i <= n;)
    {
        int j = i;
        while (j <= n && a[j].u == a[i].u)
            j++;
        int sum = j - i;
        int mx2 = min(sum, mid - now);

        int cnt = 0;
        for (int c = mx2; c >= 1; c--)
        {
            if (a[i + c - 1].v >= mid - now - c)
            {
                cnt = c;
                break;
            }
        }
        now += cnt;

        if (now >= mid)
            return 1;

        i = j;
    }
    return 0;
}

void solve()
{
    cin >> n;
    a.resize(n + 1);
    mx = 0;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i].u >> a[i].v;
        a[i].id = i;
        mx = max(mx, a[i].u);
    }

    sort(a.begin() + 1, a.end(), cmp);

    l = 1, r = n;
    int res = 1;
    while (l <= r)
    {
        int mid = l + ((r - l) >> 1);
        if (check(mid))
        {
            res = max(mid, res);
            l = mid + 1;
        }
        else
        {
            r = mid - 1;
        }
    }
    cout << res << endl;

    int now = 0;
    vi ans;
    for (int i = 1; i <= n;)
    {
        int j = i;
        while (j <= n && a[j].u == a[i].u)
            j++;
        int sum = j - i;

        int mx2 = min(sum, res - now);
        int cnt = 0;

        for (int c = mx2; c >= 1; c--)
        {
            if (a[i + c - 1].v >= res - now - c)
            {
                cnt = c;
                break;
            }
        }

        for (int k = 0; k < cnt; k++)
        {
            ans.push_back(a[i + k].id);
        }

        now += cnt;
        if (now >= res)
            break;
        i = j;
    }

    for (int i = 0; i < res; i++)
    {
        cout << ans[i] << ' ';
    }
    cout << endl;
}

I. Square Puzzle

知识点:搜索,暴力

思路:一共有七种操作,移动每一行每一列以及旋转。进行 \(bfs\),把状态当作字符串存到桶里,同时记录到达这个状态的最小操作次数。

Code
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define vi vector<int>
#define vb vector<bool>
#define endl '\n'
const int M = 2e5 + 7;
string s;
const int N = 1e6 + 5;
unordered_map<string, int> ans;
queue<string> q;
string youyi(string a, int op)
{
    if (op == 1)
    {
        char t = a[0];
        a[0] = a[2], a[2] = a[1], a[1] = t;
        return a;
    }
    if (op == 2)
    {
        char t = a[3];
        a[3] = a[5], a[5] = a[4], a[4] = t;
        return a;
    }
    if (op == 3)
    {
        char t = a[6];
        a[6] = a[8], a[8] = a[7], a[7] = t;
        return a;
    }
    return a;
}
string xiayi(string a, int op)
{
    if (op == 1)
    {
        char t = a[0];
        a[0] = a[6], a[6] = a[3], a[3] = t;
        return a;
    }
    if (op == 2)
    {
        char t = a[1];
        a[1] = a[7], a[7] = a[4], a[4] = t;
        return a;
    }
    if (op == 3)
    {
        char t = a[2];
        a[2] = a[8], a[8] = a[5], a[5] = t;
        return a;
    }
    return a;
}
string xuanzhuan(string a)
{
    char t = a[0];
    a[0] = a[6], a[6] = a[8], a[8] = a[2], a[2] = t;
    t = a[1];
    a[1] = a[3], a[3] = a[7], a[7] = a[5], a[5] = t;
    return a;
}
void bfs()
{
    q.push("123456789");
    ans["123456789"] = 0;
    while (q.size())
    {
        string g = q.front();
        q.pop();
        string t;
        t = youyi(g, 1);
        if (!ans.count(t))
            q.push(t), ans[t] = ans[g] + 1;
        t = youyi(g, 2);
        if (!ans.count(t))
            q.push(t), ans[t] = ans[g] + 1;
        t = youyi(g, 3);
        if (!ans.count(t))
            q.push(t), ans[t] = ans[g] + 1;
        t = xiayi(g, 1);
        if (!ans.count(t))
            q.push(t), ans[t] = ans[g] + 1;
        t = xiayi(g, 2);
        if (!ans.count(t))
            q.push(t), ans[t] = ans[g] + 1;
        t = xiayi(g, 3);
        if (!ans.count(t))
            q.push(t), ans[t] = ans[g] + 1;
        t = xuanzhuan(g);
        if (!ans.count(t))
            q.push(t), ans[t] = ans[g] + 1;
    }
}
void solve()
{
    string s;
    map<char, char> mp;
    for (int i = 1; i <= 9; i++)
    {
        char ch;
        cin >> ch;
        mp[ch] = '0' + i;
    }
    for (int i = 1; i <= 9; i++)
    {
        char ch;
        cin >> ch;
        s += mp[ch];
    }
    if (s == "123456789")
        cout << "0" << endl;
    else if (ans[s] == 0)
        cout << -1 << endl;
    else
        cout << ans[s] << endl;
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int qwq = 1;
    bfs();
    cin >> qwq;
    while (qwq--)
    {
        solve();
    }
    return 0;
}

E. Greatest Common Divisor

知识点:贪心,数论,调和级数

思路:序列的最大公因数一定是序列和的因数
从大到小枚举序列和的因数,第一个满足条件的就是答案
如何证明当前因数是合法的?假设枚举的因数是 \(g\) 对于每一个数,都应该加到 \(g\)
的倍数,也就是加到离他最近的 \(g\) 的倍数,如果实现代价小于 \(k\),根据同余原理,剩下的操作次数一定是 \(g\) 的倍数,随即加到一个大数上即可,不会改变全序列的 \(gcd\),所以合法条件是 操作次数小于 \(k\).
如果对于每一个因数都去遍历代价数组,时间复杂度太高,需要预处理,所以我们分段来求解,如果最大值小于 \(g\),那么最优解是把所有数变成 \(g\),如果最大值大于 \(g\),那么把它按 \(g\) 的倍数分成若干段,开一个个数前缀和和一个数值前缀和进行预处理,这样分段求解的时间复杂度是调和级数,可以接受

Code
点击查看代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define endl '\n'
#define i64 long long
#define int long long
#define ui64 unsigned long long
#define vi vector<int>
#define vs vector<string>
#define vvi vector<vector<int>>
#define vb vector<bool>
#define pii pair<int, int>
#define all(a) a.begin(), a.end()
#define ull unsigned long long
#define pb push_back
const int M = 2e5 + 7;
const int mod = 998244353;
const int MOD = 1e9 + 7;
const int INF = 1e9;
const double pi = acos(-1.0);
int inf = -1e18;
// int dir[8][2] = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}, {1, 1}, {-1, -1}, {1, -1}, {-1, 1}};
int dir[4][2] = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}};
i64 n, m, k = 0, x, y, l, r;
string s;
int gcd(int a, int b)
{
    return b == 0 ? a : gcd(b, a % b);
}
int lcm(int a, int b)
{
    return a * b / gcd(a, b);
}
int ksm(int a, int b, int p)
{
    int ans = 1;
    a %= p;
    while (b)
    {
        if (b & 1)
        {
            ans = ans * a % p;
        }
        a = a * a % p;
        b >>= 1;
    }
    return ans;
}
int gsc(int a, int b, int p)
{
    a %= p;
    int res = 0;
    while (b > 0)
    {
        if (b & 1)
        {
            res = (res + a) % p;
        }
        a = (a + a) % p;
        b >>= 1;
    }
    return res;
}

// 并查集
// int fa[M];
//  void init()
//  {
//      for(int i=1;i<M;i++)
//      {
//          fa[i]=i;
//      }
//  }
//  int find(int a)
//  {
//      return a==fa[a]?a:fa[a]=find(fa[a]);
//  }
//  bool same(int a,int b)
//  {
//      return find(a)==find(b);
//  }
//  void join(int a,int b)
//  {
//      a=find(a);
//      b=find(b);
//      if(a!=b)
//      {
//          fa[a]=b;
//      }
//  }

// 树状数组
// int tr[M];
// int lb(int i)
// {
//     return i & (-i);
// }
// void add(int i, int x)
// {
//     for (; i <= n; i += lb(i))
//     {
//         tr[i] += x;
//     }
// }
// int sum(int i)
// {
//     int ans = 0;
//     for (; i > 0; i -= lb(i))
//     {
//         ans += tr[i];
//     }
//     return ans;
// }
// int que(int l, int r)
// {
//     return sum(r) - sum(l - 1);
// }

// 欧拉筛
//  int c = 0;
//  int pri[M];
//  int vis[M];
//  void ols(int n)
//  {
//      for (ll i = 2; i <= n; i++)
//      {
//          if (!vis[i])
//          {
//              pri[++c] = i;
//          }
//          for (ll j = 1; (ll)(i * pri[j]) <= n; j++)
//          {
//              vis[i * pri[j]] = 1;
//              if (i % pri[j] == 0)
//              {
//                  break;
//              }
//          }
//      }
//  }
// tarjan
// vi adj[M];
// int dfn[M], tim = 0, low[M];
// int stk[M], vis[M], pos = 0;
// int c = 0, scc[M], siz[M];
// void tarjan(int x)
// {
//     tim++;
//     dfn[x] = tim;
//     low[x] = tim;
//     for (int i : adj[x])
//     {
//         if (!dfn[i])
//         {
//             tarjan(i);
//             low[i] = min(low[i], low[x]);
//         }
//         else if (vis[i])
//         {
//             low[i] = min(low[i], dfn[x]);
//         }
//     }
//     if (dfn[x] == low[x])
//     {
//         c++;
//         int now;
//         do
//         {
//             now = stk[pos];
//             pos--;
//             vis[now] = 0;
//             scc[now] = c;
//             siz[c]++;
//         } while (now != x);
//     }
// }
// 字典树
// int tr[3000005][63];
// int c = 1;
// int p[3000005];
// int path(char ch)
// {
//     if (islower(ch))
//     {
//         return ch - 'a';
//     }
//     else if (isupper(ch))
//     {
//         return ch + 26 - 'A';
//     }
//     return ch + 52 - '0';
// }
// void insert(string word)
// {
//     int cur = 1;
//     p[cur]++;
//     for (int i = 0; i < word.size(); i++)
//     {
//         int c = path(word[i]);
//         if (tr[cur][c] == 0)
//         {
//             tr[cur][c] = ++c;
//         }
//         cur = tr[cur][c];
//         p[cur]++;
//     }
// }
// int cou(string word)
// {
//     int cur = 1;
//     for (int i = 0; i < word.size(); i++)
//     {
//         int c = path(word[i]);
//         if (tr[cur][c] == 0)
//         {
//             return 0;
//         }
//         cur = tr[cur][c];
//     }
//     return p[cur];
// }
// void clear()
// {
//     for (int i = 1; i <= c; i++)
//     {
//         memset(tr[i], 0, sizeof(tr[i]));
//         p[i] = 0;
//     }
//     c = 1;
// }
void solve()
{
    cin >> n >> k;

    vi a(n);
    int sum = 0;
    int mx = 0;
    for (int i = 0; i < n; i++)
    {
        cin >> a[i];
        sum += a[i];
        mx = max(mx, a[i]);
    }
    int sum0 = sum + k;
    vi b;
    for (long long i = 1; i * i <= sum0; i++)
    {
        if (sum0 % i == 0)
        {
            b.push_back(i);
            if (i * i != sum0)
            {
                b.push_back(sum0 / i);
            }
        }
    }
    sort(b.rbegin(), b.rend());
    vi c(mx + 1, 0);
    for (int x : a)
    {
        c[x]++;
    }

    vi pc(mx + 1, 0);
    vi pv(mx + 1, 0);
    for (int i = 1; i <= mx; i++)
    {
        pc[i] = pc[i - 1] + c[i];
        pv[i] = pv[i - 1] + i * c[i];
    }
    for (auto i : b)
    {
        if (i > mx)
        {
            if (n * i <= sum0)
            {
                cout << i << endl;
                return;
            }
        }
        else
        {
            int now = 0;
            for (int j = 0; j <= mx; j += i)
            {
                l = j + 1;
                r = min(mx, j + i - 1);

                if (l <= r)
                {
                    int t1 = pc[r] - pc[l - 1];
                    int t2 = pv[r] - pv[l - 1];
                    now += (j + i) * t1 - t2;
                }
            }
            if (now <= k)
            {
                cout << i << endl;
                return;
            }
        }
    }
}
signed main()
{

    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int qwq = 1;
    cin >> qwq;
    /*
    while (cin >> ws && cin.good())
    {
         solve();
    }
    */
    while (qwq--)
    {
        solve();
    }
    return 0;
}

posted @ 2026-04-27 17:30  Lambda_L  阅读(23)  评论(0)    收藏  举报