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个数
- 不换
- 直接后缀和
两种情况取最大值即可
#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\)
联立解方程组即可,最后得到
故需要满足
#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个二维坐标点。
可以发现,精灵之光的状态就只有两种:开/关
选择相同坐标进行偶数次操作等于什么都没做,显然和异或的运算性质是一样的
设 X 为所有亮点的\(x\)异或和
最初
那么对于每一次操作选择的 \((x_i,y_i)\) 我们可以得到
所以不管进行多少次操作,X的值不会改变
现在我们想要求Y的值,那按照相似的思路,我们也需要找到另一个关于Y的不变量,最好包含X。
最简单的就是 所有亮点的\(x+y\)异或和
设XPY 为所有亮点的\(x+y\)异或和
手写一下可以发现
初始
对于每一次操作选择的 \((x_i,y_i)\) 我们可以得到
所以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\)就行
- Alice 有 \(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;
}
浙公网安备 33010602011771号