12025 ACM普及赛题解V.0.9

12025 ACM普及赛题解


目录

Cloudflare验证

测试你是不是真人


#include<bits/stdc++.h>
using namespace std;
using ll = long long;
ll a,b;

int main(){
    cin >> a >> b;
    cout<<a+b;
    return 0;
}

回文映射代数与生成元

题面很复杂,其实题意就是

  • 对于给定的k 判断是否对于所有 n \(\geq\) k 存在一个长度为k的回文数组,并且数组的和为n;

(其实观察样例可以猜)

那么我们可以玩小样例,

k=1, 肯定可以 ,\(A=[n]\) 即可 ,\(YES\)
k=2 , \(A\)形如\([x,x]\) , \(Sum(A)=2x\) ,显然2x是偶数,只有\(n\)为偶数时可以,故\(NO\)
k=3 , \(A\)形如\([x,y,x]\) , \(Sum(A)=2x+y\) ,显然2x+y可以表示\(\geq3\)的任意数,所以可以 ,\(YES\)
以此类推...

综上

k为奇数时可以,k为偶数时不可以

#include <bits/stdc++.h>

using namespace std;
using ll = long long;
void sol()
{

    int n;
    cin >> n;
    cout << ((n & 1) ? "YES" : "NO") << endl;
}

int main()
{
    int t;
    cin >> t;
    while (t--)
        sol();

    return 0;
}


[IOI国家集训队2024]

题意:给定n个数,问最多能分成多少组,每组至少一个数,且每组的最小值 \(×\) 组内人数 \(>= x\)

人类的本能是贪心()

因为我们可以自由分配每一个人,所以先将所有数从大到小排序
显然,如果一个人实力>=x,那么他可以单独成组

如果一个人实力< x,那么他必须和其他人一起成组
所以我们可以从大到小遍历每一个人,贪心地组队。

如果你不知道怎么排序,可以做两次,从大到小贪心一次得到ans1,从小到大得到ans2,最后取max(ans1, ans2),此事在区域赛中亦有记载


#include <bits/stdc++.h>
using namespace std;
using ll = long long;

void sol()
{
    ll n, x;
    cin >> n >> x;
    vector<ll> a(n);

    for (ll &i : a)
        cin >> i;

    sort(a.begin(), a.end(), greater<ll>());

    ll ans = 0, cnt = 0;
    for (ll i = 0; i < n; i++)
    {
        if (a[i] >= x)
            ++ans;
        else
        {
            ++cnt;
            if (cnt * a[i] >= x)
            {
                ++ans;
                cnt = 0;
            }
        }
    }
    cout << ans << endl;
}
int main()
{

    int t;
    cin >> t;
    while (t--)
        sol();
    return 0;
}

任务:拯救难民

题意:给定n个单元,单元i距离出口n-i公里,单元i有一个传送装置可以传送到单元ai。每个人都必须使用当前传送装置k次,且不能传送到当前所在单元。求最小的距离之和。


#include <bits/stdc++.h>

using namespace std;
using ll = long long;
void sol()
{
    ll n, k;
    cin >> n >> k;
    vector<int> a(n + 1);
   
    a[n] = n - 1;
    a[n - 1] = n;

    if (k & 1)
        for (int i = n - 2; i >= 1; i--)
            a[i] = n;
    else
        for (int i = n - 2; i >= 1; i--)
            a[i] = n - 1;

    for (int i = 1; i <= n; ++i)
        cout << a[i] << " ";
    cout << endl;
}
int main()
{
    int t;
    cin >> t;
    while (t--)
        sol();
    return 0;
}

又一个数组问题

每次可以选一个数放到最后,计算最后k个数的和(要尽可能大),然后把这个数放回原位。需要为每个k=1到n都算出这个最大和。


n轮相互独立, 所以每轮单独考虑

对于每一轮:

    • 在后缀和前面选一个最大的数放到最后,挤掉了第k个数
  1. 不换
    • 直接后缀和

两种情况取最大值即可

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void sol()
{

    ll n;
    cin >> n;
    vector<ll> a(n + 2, 0), maxx(n + 2, 0), pre(n + 2, 0);
    for (int i = 1; i <= n; i++)
        cin >> a[i];

    for (int i = 1; i <= n; i++)
    {
        maxx[i] = max(maxx[i - 1], a[i]);
        pre[i] = pre[i - 1] + a[i];
    }

    for (int i = 1; i <= n; ++i)
    {
        ll pref = pre[n] - pre[n - i];
        ll modify=max(0ll,maxx[n - i] - a[n - i + 1]);

        cout << pref + modify<< ' ';
    }
    cout << endl;
}

int main()
{
    int t;
    cin >> t;
    while (t--)
        sol();
    return 0;
}


打扫实验室

题意:给定a,b,c,问是否存在正整数x,y 使得
\(a+x = b+y = c-x-y\)

联立解方程组即可,最后得到

\[y = \frac{c - 2b + a}{3} \\ \]

故需要满足

\[(c - 2b + a) \% 3 == 0 \\ (c - 2b + a) \geq 0 \\ \]

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void sol()
{
    ll a, b, c;
    cin >> a >> b >> c;
    ll tmp = c + a - 2 * b;
    if (tmp % 3 != 0 or tmp < 0)
        cout << "NO" << endl;
    else
        cout << "YES" << endl;
}

int main()
{
    int t;
    cin >> t;
    while (t--)
        sol();
    return 0;
}

远古遗迹

首先肯定是选已有的一个颜色作为最终的颜色。
然后考虑怎么算代价。
对于一种颜色来讲,如果所有格子都不相邻,那么肯定可以一次全选择,也就是一次操作就全部选完,否则,有相邻的话就需要两次,可以证明要么是1次,要么是2次。

总共的操作次数就是:所有颜色的操作次数之和\(-\)最大的操作次数


#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ld = long double;

void sol()
{

    int n, m;
    cin >> n >> m;
    map<int, int> mp;
    vector<vector<int>> a(n + 5, vector<int>(m + 5, 0));
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
        {
            cin >> a[i][j];
            mp[a[i][j]] = 1;
        }

    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            if (a[i][j] == a[i - 1][j] or a[i][j] == a[i][j - 1] or a[i][j] == a[i + 1][j] or a[i][j] == a[i][j + 1])
                mp[a[i][j]] = 2;

    int ans = 0;
    int mx = 0;
    for (auto it : mp)
    {
        ans += it.second;
        mx = max(mx, it.second);
    }
    cout << ans - mx << endl;
}

int main()
{
    int t;
    cin >> t;
    while (t--)
        sol();
    return 0;
}

[E3-2016]寻找森林之心

题意:问哪个最初的点可以结果任意次操作得到给定n个二维坐标点。

可以发现,精灵之光的状态就只有两种:开/关
选择相同坐标进行偶数次操作等于什么都没做,显然和异或的运算性质是一样的

\[ 0 \oplus 0 = 0 \\ 0 \oplus 1 = 1 \\ 1 \oplus 0 = 1 \\ 1 \oplus 1 = 0 \\ \]

X 为所有亮点的\(x\)异或和
最初

\[X =x_{ori} \]

那么对于每一次操作选择的 \((x_i,y_i)\) 我们可以得到

\[X = X \oplus (x) \oplus (x+1) \oplus (x)\oplus (x+1) =X=x_{ori} \]

所以不管进行多少次操作,X的值不会改变

现在我们想要求Y的值,那按照相似的思路,我们也需要找到另一个关于Y的不变量,最好包含X。

最简单的就是 所有亮点的\(x+y\)异或和

设XPY 为所有亮点的\(x+y\)异或和

手写一下可以发现
初始

\[XPY = x_{ori} + y_{ori} \]

对于每一次操作选择的 \((x_i,y_i)\) 我们可以得到

\[XPY = XPY \oplus (x+y) \oplus (x+y+1) \oplus (x+y) \oplus (x+y+1) \]

所以XPY的值也不会改变

所以我们可以得到
\(y_{ori} = XPY - X\)

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void sol()
{
    int n;
    cin >> n;
    ll x = 0, xpy = 0;
    for (int i = 0; i < n; ++i)
    {
        ll a, b;
        cin >> a >> b;
        x ^= a;
        xpy ^= (a + b);
    }

    ll y = xpy - x;
    cout << x << " " << y << endl;
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    int t;
    cin >> t;
    while (t--)
        sol();
    return 0;
}

光圈科技测试

模拟题
根据题意模拟即可
关键点在于如何模拟?

重点是处理2操作的翻转,肯定不能真的翻转,得有更聪明的操作,最好能\(O(1)\)的时间复杂度解决
于是我们可以维护两个数组

  • a 存正的数组,b存reverse的a数组

请看代码:


#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
using ld = long double;
using pll = pair<ll, ll>;
using pii = pair<int, int>;

void sol()
{
    ull q;
    cin >> q;

    deque<ull> a, b; //a 存正的数组,b存reverse的a数组
    ull ansa = 0;    // a的震荡值
    ull ansb = 0;   // b的震荡值

    ull tot = 0; //数组元素和
    ull n = 0;//元素个数  

    while (q--)
    {

        int op;
        cin >> op;
        if (op == 1)
        {

            ull tmp = tot - a.back();

            ansa += tmp;
            ansa -= a.back() * (n - 1);

            ansb -= tmp;
            ansb += a.back() * (n - 1);

            ull xxx = a.back();
            a.push_front(xxx);

            b.push_back(xxx);
            a.pop_back();
            b.pop_front();
        }

        else if (op == 2)
        {
            swap(a, b);
            swap(ansa, ansb);
        }
        else
        {
            ++n;
            ull x;
            cin >> x;

            a.push_back(x);
            tot += x;
            b.push_front(x);

            ansa += x * n;
            ansb += tot;
        }

        cout << ansa << endl;
    }
}
int main()
{

    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while (t--)
        sol();
    return 0;
}

数论考试题

题意:

一个由 \(n!\)\(d\) 组成的数,能不能被 1 3 5 7 9整除

对于 3 ,各位数之和能被 3 整除代表这个数能被 3 整除,因此 只要 d 能被 3 整除或者 \(n>=3\) 即可。因为 \(n>=3\) 时,\(n!\) 都是 3 的倍数。

对于 5 ,只要看d是不是 5 即可。

对于 9 ,各位数之和能被 9 整除代表这个数能被 9 整除。

\(n<=2\) 时,需满足 d 能被 9 整除;

\(3<=n<6\) 时,\(n!\) 是 3 的倍数而不是 9 的倍数,需满足 d能被 3 整除;
\(n>=6\) 时,\(n!\) 是 9 的倍数。

对于 7 ,经过打表可以发现,当 \(n>=3\) 时,一定能被 7 整除。

代码如下:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void solve() {
  int t;
  cin >> t;
  while (t--) {
    ll n, d;
    cin >> n >> d;
    cout << 1 << ' ';
    if (d % 3 == 0 || n >= 3)
      cout << 3 << ' ';
    if (d == 5)
      cout << 5 << ' ';
    if (d == 7 || n >= 3)
      cout << 7 << ' ';
    if ((d==9&&n<=2)||(d%3==0&&n>=3&&n<6)||(n>=6))
      cout<<9<<' ';
    cout<<endl;
  }
}

int main() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  cout.tie(nullptr);
  solve();
  return 0;
}

微云算

题意 :

给定一个由 0 和 1 组成的字符串,轮流在任意两个数字之间加 and 或者 or,看最后转变成 1 还是 0.

不难发现,Alice只会加 or ,Bob 只会加 and。

而Alice确保赢的办法就是产生 1 or ...... ,...... or 1 或者 ...... or 1 or ......的结构,除此之外的结构都会被Bob通过在 1 和 0 之间加 and 破坏掉。

又由于Alice先手,所以对于前两种结构,只要看首尾是不是 1 就行。对于第三种结构,通过演算可以发现,只要存在两个连续的 1 ,就一定可以产生 or 1 or,因为Alice可以在连续的两个 1 中间加 or 。

代码如下:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void solve() {
    int t;
    cin >> t;
    while (t--) {
        ll n;
        string s;
        cin >> n >> s;
        if (s[0] == '1' || s.back() == '1')
            cout << "YES" << endl;
        else {
            int z = 0, ans = 0;
            for (auto i : s) 
                if (i == '1' && z == 1) {
                    cout << "YES" << endl;
                    ans = 1;
                    break;
                }else if (i == '1') 
                    z = 1;
                else 
                    z = 0;
                
            
            if (!ans) cout << "NO" << endl;
        }
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    solve();
    return 0;
}

骑士比武

1 号牌只能打败n号牌
因此我们可以考虑分类讨论1,n号牌给了谁

先特判一张牌的情况

之后只用考虑两个人都至少有两张牌的情况

  • \(1 , n\) 在同一个人手上

    • 显然这个人只需要不停出n就赢了
  • \(1 , n\) 在不同人手上

    • Alice 有 \(1\)
      • Alice 无论如何先手不能出1
      • Bob出n就赢了
    • Alice 有 \(n\)
      • Alice 先手不能出\(n\),他会出剩余的最大值
      • Bob必须要比这个值大才能赢
      • 所以看谁有\(n-1\)就行

代码写得有点丑,但是逻辑比较简单


#include <bits/stdc++.h>
 
using namespace std;
using ll = long long;
 
using namespace std;
void sol()
{
 
    int n;
    cin >> n;
    vector<int> AAA, BBB;
    string s;
    cin >> s;
    int cnt = 1;
    for (auto i : s)
    {
        if (i == 'A')
            AAA.push_back(cnt);
        else
            BBB.push_back(cnt);
        ++cnt;
    }
    if (AAA.size() == 1 and BBB.size() == 1)
    {
        if (AAA[0] < BBB[0])
            cout << "Alice" << endl;
        else
            cout << "Bob" << endl;
        return;
    }
 
    if (AAA.size() == 1)
    {
        cout << "Bob" << endl;
        return;
    }
    if (BBB.size() == 1)
    {
        cout << "Alice" << endl;
        return;
    }
 
    if (AAA[0] == 1 and AAA[AAA.size() - 1] == n)
    {
        cout << "Alice" << endl;
        return;
    }
    if (BBB[0] == 1 and BBB[BBB.size() - 1] == n)
    {
        cout << "Bob" << endl;
        return;
    }
 
    if (AAA[0] == 1)
    {
        cout << "Bob" << endl;
        return;
    }
    else
    {
        if (AAA[AAA.size() - 2] > BBB[BBB.size() - 1])
        {
            cout << "Alice" << endl;
            return;
        }
        else
        {
            cout << "Bob" << endl;
            return;
        }
    }
}
 
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
 
    int t;
    cin >> t;
    while (t--)
        sol();
    return 0;
}

守望先锋

题意,最大化收益,预处理+01背包dp
直接套板子

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pll = pair<ll, ll>;
// WAitWhat
ll cnt[1025];

void dfs(ll x, ll now)
{
    if (x > 1024)
        return;
    if (cnt[x] > 0)
    {
        if (cnt[x] <= now)
            return;
        cnt[x] = min(cnt[x], now);
    }
    else
        cnt[x] = now;

    for (int i = 1; i <= x; ++i)
        dfs(x + x / i, now + 1);

}
void sol()
{
    ll n, k;
    cin >> n >> k;
    vector<ll> w(n + 3), cost(n + 3, 0);

    ll x;
    ll tot = 0;
    for (int i = 1; i <= n; ++i)
    {
        cin >> x;
        cost[i] = cnt[x];
        tot += cost[i];
    }

    for (int i = 1; i <= n; ++i)
        cin >> w[i];

    // 01背包 费用为cost[i],价值为w[i] 最大容量为k

    k = min(k, tot);
    vector<ll> dp(k+1, 0);
    for (int i = 1; i <= n; ++i)
        for (int j = k; j >= cost[i]; --j)
            dp[j] = max(dp[j], dp[j - cost[i]] + w[i]);
    cout << dp[k] << '\n';
}

int main()
{
    memset(cnt, -1, sizeof(cnt));
    dfs(1, 0);

    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while (t--)
        sol();

    return 0;
}

后勤任务

#include <bits/stdc++.h>
using namespace std;

void test() {
    int n, k;
    cin >> n >> k;
    int m = k - 1;

    vector<int> l(n);
    for (int i = 0; i < n; i++) {
        cin >> l[i];
    }
    vector<int> r(n);
    for (int i = 0; i < n; i++) {
        cin >> r[i];
    }

    vector<int> a(n), b(n);
    long long y = 0;
    for (int i = 0; i < n; i++) {
        a[i] = max(l[i], r[i]);
        b[i] = min(l[i], r[i]);
        y += a[i];
    }

    sort(b.begin(), b.end(), greater<>());
    for (int i = 0; i < m; i++) {
        y += b[i];
    }

    long long x = y + 1;
    cout << x << '\n';
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);

    int t;
    cin >> t;
    for (int i = 0; i < t; i++) {
        test();
    }
   
    return 0;
}
posted @ 2025-05-11 16:54  aminuosi  阅读(62)  评论(0)    收藏  举报