Codeforces Round 924 (Div. 2)

写在前面

比赛地址:https://codeforces.com/contest/1928

终于把欠下的一堆题补上了呃呃

天使骚骚共通线什么构式呃呃,一周目就想走老师线直通单身笑死我了。

A

签到。

发现等分后重新拼接可以得到 x2×2yy2×2x 的两个矩形,检查它们是否与 x×yy×x 相同即可。

复制复制
//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define mp std::make_pair
#define pii std::pair<int,int>
#define LL long long
//=============================================================
//=============================================================
//=============================================================
int main() {
//freopen("1.txt", "r", stdin);
std::ios::sync_with_stdio(0), std::cin.tie(0);
int T; std::cin >> T;
while (T --) {
int x, y; std::cin >> x >> y;
std::map <pii, int> m;
m[mp(x, y)] = 1, m[mp(y, x)] = 1;
int flag = 0;
if (x % 2 == 0) {
if (m[mp(x / 2, 2 * y)] == 0) flag = 1;
}
if (y % 2 == 0) {
if (m[mp(2 * x, y / 2)] == 0) flag = 1;
}
if (flag) std::cout << "YES\n";
else std::cout << "NO\n";
/*
x, y
y, x
x/2, 2y
y/2, 2x
*/
}
return 0;
}

B

知识点:双指针。

显然重复元素只会贡献一次,于是先去重。

然后考虑对于元素 b1<b2<<bk,若它们加排列中元素后能够相等,则一定有 b1+nbk+1,即 bkb1n1。排序后双指针求最大的满足条件的区间即可。

//知识点:双指针
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
//=============================================================
//=============================================================
int main() {
//freopen("1.txt", "r", stdin);
std::ios::sync_with_stdio(0), std::cin.tie(0);
int T; std::cin >> T;
while (T --) {
int n; std::cin >> n;
std::map <int, int> m;
std::vector <int> v;
for (int i = 1; i <= n; ++ i) {
int x; std::cin >> x;
if (m.count(x)) continue;
m[x] = 1;
v.push_back(x);
}
std::sort(v.begin(), v.end());
int l = 0, r = 0;
while (r < v.size() && v[r] + 1 <= v[l] + n) ++ r;
-- r;
int ans = r - l + 1;
while (l < v.size()) {
++ l;
while (r < v.size() && v[r] + 1 <= v[l] + n) ++ r;
-- r;
ans = std::max(ans, r - l + 1);
}
std::cout << ans << "\n";
}
return 0;
}

C

知识点:数学

纯粹的数学题。

首先当 k<xkn 时显然不满足题意。

对于一满足题意的 k(kx),数列中 1 的位置可表示为 1+y×(2k2)(y0),则 x 的位置可表示为 1+(y1)×(2k2)+(x1)(y1)1+y×(2k2)(x1)(y1),讨论一下:

  • n=1+(y1)×(2k2)+(x1)(y1),则有:

    2(k1)(y1)=nx

    于是考虑枚举 nx 的所有因数 d=2×(k1) 解得 k=d2+1,若满足 xd2+1<n 且为整数则找到了一个解。

  • n=1+y×(2k2)(x1)(y1),则有:

    2(k1)y=n+x2

    同理枚举 n+x2 的所有因数求 k 的解即可。

总时间复杂度 O(tn+x) 级别。

//知识点:数学
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
//=============================================================
void Solve() {
LL x, n; std::cin >> n >> x;
LL d1 = n - x, d2 = n + x - 2;
LL ans = 0;
std::map <LL, bool> m;
if (d1 > 0) {
for (LL i = 1; i * i <= d1; ++ i) {
if (d1 % i != 0) continue;
if (!m.count(i) && (i % 2 == 0) && (i / 2 + 1 < n) && (i / 2 + 1 >= x)) {
ans ++, m[i] = 1;
}
if (!m.count(d1 / i) && ((d1 / i) % 2 == 0) && ((d1 / i) / 2 + 1 < n) && ((d1 / i) / 2 + 1 >= x)) {
ans ++, m[d1 / i] = 1;
}
}
}
if (d2 > 0) {
for (LL i = 1; i * i <= d2; ++ i) {
if (d2 % i != 0) continue;
if (!m.count(i) && i % 2 == 0 && (i / 2 + 1 < n) && (i / 2 + 1 >= x)) {
ans ++, m[i] = 1;
}
if (!m.count(d2 / i) && ((d2 / i) % 2 == 0) && ((d2 / i) / 2 + 1 < n) && ((d2 / i) / 2 + 1 >= x)) {
ans ++, m[d2 / i] = 1;
}
}
}
std::cout << ans << "\n";
}
//=============================================================
int main() {
// freopen("1.txt", "r", stdin);
std::ios::sync_with_stdio(0), std::cin.tie(0);
int T; std::cin >> T;
while (T --) {
Solve();
}
return 0;
}
/*
1
10 1
5
10 1
10 2
10 3
10 4
10 5
1 2 1 2 1 2 1 2 1 2
1 2 3 2 1 2 3 2 1 2
1 2 3 4 3 2 1 2 3 4
1 2 3 4 5 4 3 2 1 2
1 2 3 4 5 6 5 4 3 2
1 2 3 4 5 6 7 6 5 4
1 2 3 4 5 6 7 8 7 6
1 2 3 4 5 6 7 8 9 8
*/

D

知识点:三分

位于不同小组的同一种族的两个体才会产生贡献,则当小组数 k 确定时,对于每个种族最优的分配方案是将所有个体均分到 k 个小组中。则此时若某种族有 c 个个体,则有 r=cmodk 个小组有 ck+1 个个体,其他 kr 个小组有 ck 个个体,贡献即为:

f=r(ck+1)(cck1)×b+(kr)ck(cck)×b

则最终的总贡献值为:

g=i=1nfi(k1)×x

观察多项式 g 的形式显然总贡献 g 是关于小组数量 k 的单峰函数,三分求函数极值点即可。

总时间复杂度 O(nlog3max{ci}) 级别。

//知识点:三分
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 2e5 + 10;
const int kInf = 1e9 + 2077;
//=============================================================
int n, b, x, c[kN];
//=============================================================
LL Check(LL mid_) {
LL ret = -1ll * (mid_ - 1) * x, d = 0;
for (int i = 1; i <= n; ++ i) {
LL k = c[i] / mid_, r = c[i] % mid_;
d += 1ll * (mid_ - r) * k * (c[i] - k) * b;
d += 1ll * r * (k + 1) * (c[i] - k - 1) * b;
}
return ret + d / 2ll;
}
void Solve() {
std::cin >> n >> b >> x;
for (int i = 1; i <= n; ++ i) std::cin >> c[i];
std::sort(c + 1, c + n + 1);
LL ans = 0;
int l = 1, r = 2 * c[n];
for (; l < r; ) {
int mid1 = l + (r - l) / 3, mid2 = r - (r - l) / 3;;
LL ret1 = Check(mid1), ret2 = Check(mid2);
ans = std::max(ans, std::max(ret1, ret2));
if (ret1 < ret2) {
l = mid1 + 1;
} else {
r = mid2 - 1;
}
}
ans = std::max(ans, std::max(Check(l), Check(r)));
std::cout << ans << "\n";
}
//=============================================================
int main() {
// freopen("1.txt", "r", stdin);
std::ios::sync_with_stdio(0), std::cin.tie(0);
int T; std::cin >> T;
while (T --) {
Solve();
}
return 0;
}

E

知识点:DP,构造

赛时没想到能 DP 预处理,恼弹一个。

首先所有位置均有 xmody 的贡献,先令 s 与所有位置减去 n×(xmody)。此时所有位置均为 y 的倍数,若有解则 s 一定也为 y 的倍数,于是再令 s 与所有位置除以 y,则此时要构造的数列形态为:

xxmodyy,xxmodyy+1,,0,1,2,,0,1,2,

数列合法当且仅当数列之和为 s

考虑枚举第一段 xxmodyy,xxmodyy+1, 的长度 i 并检查使用剩下的位置 i+1n 构造若干 0,1, 能否凑出 s1jiaj

上述检查对象可以 DP 预处理。具体地,记 fi 表示凑出 i 最少需要多少个位置,初始化 f0=0,1is, fi=。转移时对于每个 i 枚举向后添加一段和为 j 长度为 k0,1,,则有:

fi+kfi+j(i+js)

注意为了输出方案还需要记录转移前驱。

转移过程中限制了 i+js,而 j 的值是关于 k 的二次函数,则有 1js 成立,则上述 DP 时间复杂度为 O(ss) 级别。

预处理后按照上述思路枚举第一段 xxmodyy,xxmodyy+1, 的长度 i,再检查 fs1jiajni 是否成立即可。若成立则找到一组合法构造,令前 i 个位置为枚举的第一段,[i+1,i+fs1jiaj] 为预处理的答案,剩余位置补 0,然后乘 yxmody 还原即可。

枚举的第一段长度最大为 O(s) 级别,则总时间复杂度 O(ss+n) 级别。

注意 f 不需要每组数据都进行预处理。

没开 LL WA 了发,警醒!

//知识点:DP,构造
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 2e5 + 10;
const int kInf = 1e9 + 2077;
//=============================================================
int n, x, y;
LL s;
int f[kN], from[kN], g[kN];
std::vector <int> ans;
//=============================================================
void DP(int lim_) {
f[0] = 0;
for (int i = 1; i <= lim_; ++ i) f[i] = kInf;
for (int i = 0; i <= lim_; ++ i) {
for (int j = 1, k = 1; i + j <= lim_; ++ k, j += k) {
if (f[i] + k + 1 < f[i + j]) {
f[i + j] = f[i] + k + 1;
g[i + j] = k + 1;
from[i + j] = i;
}
}
}
}
void Solve() {
std::cin >> n >> x >> y >> s;
s -= 1ll * n * (x % y);
if (s < 0 || s % y != 0) {
std::cout << "NO\n";
return ;
}
int r = x % y;
x = (x - r) / y, s /= y;
ans.clear();
ans.push_back(x);
LL s1 = x;
for (int i = 1; i <= n; ++ i) {
if (s1 > s) break;
if (f[s - s1] <= n - i) {
std::cout << "YES\n";
for (int j = s - s1; j; j = from[j]) {
for (int k = 1; k <= g[j]; ++ k) {
ans.push_back(k - 1);
}
}
while ((int) ans.size() < n) ans.push_back(0);
for (auto j: ans) std::cout << r + j * y << " ";
std::cout << "\n";
return;
}
ans.push_back(x + i);
s1 += x + i;
}
std::cout << "NO\n";
}
//=============================================================
int main() {
// freopen("1.txt", "r", stdin);
std::ios::sync_with_stdio(0), std::cin.tie(0);
int T; std::cin >> T;
DP(kN - 10);
while (T --) Solve();
return 0;
}
/*
1
1 1 2 1
*/

F

gugugu~

写在最后

学到了什么:

  • D:若可将贡献写成多项式形式,观察性质。
  • E:DP 预处理构造方案,开 LL
posted @   Luckyblock  阅读(58)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示