Codeforces Round 963 (Div. 2)

写在前面

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

妈的睡到 22:35 整点起床,刚下床就开了妈的太刺激!

为了保证队长当前是 1k9 这个事实不变方便劝诱新大神,于是上小号了呃呃,D 调出来了不是太烂感觉暑假肯定能把小号也打上紫嘻嘻

唉反正小号随便打了呃呃

置顶广告:中南大学 ACM 集训队绝赞招新中!

有信息奥赛基础,获得 NOIP 省一等奖并达到 Codeforces rating 1900+ 或同等水平及以上者,可以直接私聊我与校队队长联系,免选拔直接进校集训队参加区域赛!

没有达到该水平但有志于 XPCX 赛事请关注每学年开始的 ACM 校队招新喵!

到这个时候了还缺队友实在不妙!求求求求快来个大神带我呜呜呜呜

A

签到。

复制复制
//
/*
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::string s; std::cin >> s;
int ans = 0, cnt1 = 0, cnt[4] = {n, n, n, n};
for (auto ch: s) {
if (ch == '?') ++ cnt1;
else if (cnt[ch - 'A']) -- cnt[ch - 'A'], ++ ans;
}
std::cout << ans << "\n";
}
return 0;
}

B

模拟。

呃呃反正我是模拟写的,应该有更简单的直接判定的方法。

先特判一开始就合法。然后考虑到奇数+偶数=奇数,则考虑如何将所有偶数变为奇数。观察样例可知,操作次数不会超过偶数个数 +1 次,因为可以选择任意奇数与最大的偶数操作两次,可以造出一个比任何偶数都大的奇数,再使用该奇数操作即可。

则操作次数仅可能为偶数个数,或偶数个数+1。

考虑答案能否取到下界。操作次数为 O(n) 级别则可直接模拟。显然每次操作一定会使用当前最大的奇数进行操作,考虑记录所有偶数并升序排序,然后按顺序使用最大的奇数对所有数操作,并更新最大奇数即可。若在此过程中较小值一直为偶数一方则可取到下界。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define int long long
const int kInf = 1e9 + 2077;
//=============================================================
//=============================================================
//=============================================================
signed 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::vector<int> even;
int cnt[2] = {0}, maxodd = 0;;
for (int i = 1; i <= n; ++ i) {
int x; std::cin >> x;
if (x % 2 == 1 && x > maxodd) maxodd = x;
if (x % 2 == 0) even.push_back(x);
++ cnt[x % 2];
}
if (cnt[0] == 0 || cnt[1] == 0) {
std::cout << 0 << "\n";
continue;
}
std::sort(even.begin(), even.end());
int flag = 0;
for (auto x: even) {
if (x < maxodd) maxodd = x + maxodd;
else flag = 1;
if (x > even.back()) break;
}
std::cout << cnt[0] + flag << "\n";
}
return 0;
}
/*
1
5
999999996 999999997 999999998 999999999 1000000000
*/

C

乱搞。

感觉有一万种方法能过呃呃,我这种属于是最烂的。

发现有 kn,则第一次全部灯亮一定出现在 maxiai 时刻之后,所有灯变化次数不超过 1 次的时间范围内。于是直接记录每盏灯下一个状态变化的时刻,考虑时刻的变化大力模拟了呃呃,推荐指数不推荐。

复杂度 O(n) 级别。

正确性应该没错吧大概不会被叉吧呃呃呃,反正 system testing 是过了哈哈不过这个 system testing O(nk) 都放过去了真是不好说、、、

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 2e5 + 10;
//=============================================================
int n, k, a[kN];
std::vector<int> on, off;
//=============================================================
//=============================================================
int main() {
// freopen("1.txt", "r", stdin);
std::ios::sync_with_stdio(0), std::cin.tie(0);
int T; std::cin >> T;
while (T --) {
std::cin >> n >> k;
on.clear(), off.clear();
for (int i = 1; i <= n; ++ i) std::cin >> a[i];
std::sort(a + 1, a + n + 1);
for (int i = 1; i <= n; ++ i) {
int d = a[n] - a[i], t = d / k;
if (t % 2 == 0) on.push_back(a[n] + k - d % k);
else off.push_back(a[n] + k - d % k);
}
if (on.size() == n) {
std::cout << a[n] << "\n";
continue;
}
std::sort(on.begin(), on.end());
std::sort(off.begin(), off.end());
int ans = -1, szon = on.size(), szoff = off.size();
int realszon = szon, realszoff = szoff;
for (int now = 1, i = 0, j = 0, t = a[n]; now <= n; ++ now) {
if (i == szon) {
t = off[j];
on.push_back(off[j] + k);
++ szon;
++ realszon, -- realszoff;
++ j;
} else if (j == szoff) {
t = on[i];
off.push_back(on[i] + k);
++ szoff;
++ realszoff, -- realszon;
++ i;
} else {
if (on[i] < off[j]) {
t = on[i];
off.push_back(on[i] + k);
++ szoff;
++ realszoff, -- realszon;
++ i;
} else {
t = off[j];
on.push_back(off[j] + k);
++ szon;
++ realszon, -- realszoff;
++ j;
}
}
int ok = 1;
if (i < szon) if (on[i] == t) ok = 0;
if (j < szoff) if (off[j] == t) ok = 0;
if (ok && realszon == n) {
ans = t;
break;
}
}
std::cout << ans << "\n";
}
return 0;
}

D

二分答案,最长上升子序列。

令下标从 0 开始编号。每次删保证删除的是长度为 k 的子区间,即每次删除的数的下标 modk 恰好构成了 modk 的一个剩余系,手玩下很容易发现,若最终剩下 n 个数,其下标 modk 按顺序一定为 0,1,,n1

中位数为 x 的必要条件是数列中不小于 x 的数不少于 n2 个,发现答案有单调性,考虑二分答案并每次检查枚举量 lim 是否合法,即检查是否存在某个子序列 L={al0,al1,,aln1},有 0in1,limodk=i,li1<li,且 L 中有不少于 n2 个不小于 lim 的数。

发现 li1<li 等价于选择的数的下标 imodk 一个 0n1 的最长上升子序列。则选择不小于 lim 的数时,仅需保证它们的下标 imodk 的值是严格单调递增的,即可保证存在一种操作方案使它们出现在同一个 L 中。

于是每次 check 时,考虑对 a 中所有不小于 limimodkn1 的位置 i,记录 imodk 的值并求最长上升子序列,其长度 len 即为子序列 L 中不小于 lim 的数的最大数量,检查是否有 lenn2 即可。

总时间复杂度 O(nlogvlogn) 级别,常数非常小大概不会被叉哈哈。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 5e5 + 10;
//=============================================================
int n, k, nn;
LL a[kN], ans;
//=============================================================
namespace Bit {
#define lowbit(x) ((x)&(-x))
const int kLim = kN << 1;
int lim, nowtime, t[kLim], tim[kLim];
void Init(int lim_) {
lim = lim_, ++ nowtime;
}
void Insert(int pos_, int val_) {
for (int i = pos_; i <= lim; i += lowbit(i)) {
if (tim[i] != nowtime) t[i] = 0, tim[i] = nowtime;
t[i] = std::max(t[i], val_);
}
}
int Max(int pos_) {
int ret = 0;
for (int i = pos_; i; i -= lowbit(i)) {
if (tim[i] != nowtime) t[i] = 0, tim[i] = nowtime;
ret = std::max(ret, t[i]);
}
return ret;
}
}
bool check(LL lim_) {
std::vector<int> pos {-1};
for (int i = 0; i < n; ++ i) {
if (a[i] >= lim_ && i % k < nn)
pos.push_back(i % k + 1);
}
int len = 0;
Bit::Init(k + 1);
for (int i = 1, sz = pos.size(); i < sz; ++ i) {
int ret = Bit::Max(pos[i] - 1) + 1;
Bit::Insert(pos[i], ret);
len = std::max(len, ret);
}
return len >= (nn / 2 + 1);
}
//=============================================================
int main() {
// freopen("1.txt", "r", stdin);
std::ios::sync_with_stdio(0), std::cin.tie(0);
int T; std::cin >> T;
while (T --) {
std::cin >> n >> k;
for (int i = 0; i < n; ++ i) std::cin >> a[i];
if (n % k == 0) nn = k;
else nn = (n % k);
ans = 0;
for (LL l = 1, r = 1e9; l <= r; ) {
LL mid = (l + r) >> 1ll;
if (check(mid)) {
l = mid + 1;
ans = mid;
} else {
r = mid - 1;
}
}
std::cout << ans << "\n";
}
return 0;
}
/*
1
4 3
3 9 9 2
5 3
3 2 5 6 4
7 1
5 9 2 6 5 4 6
8 2
7 1 2 6 8 3 4 5
4 5
3 4 5 6
*/

E

结论,异或,状压 DP

先考虑一维的情况。

若只有一维,每次操作的结果和 [AGC016D] XOR Replace 是一样的。对 ai 进行一次操作相当于令 ai:=1inai,再对 j 进行一次操作相当于令 aj:=ai

则题意等价于有一个长度为 n+1 的数列 aan+1=1inai,可以任意交换 aian+1,对于 a1an 求相邻两项差的绝对值之和的最小值。

发现数据范围很小,且贡献仅与相邻元素有关,考虑状压 DP 构造数列,记 fs,i 表示当前填入的数列元素集合为 s,填入的最后一个数是 ai 时美丽值的最小值。初始化 fs,i=,f{i},i=0,则有显然的转移:

1i,jn+1,ij,is,  fs,ifs{j},j+|aiaj|

记全集为 S,答案即为:

min1i,jn+1,ijfS{i},j

总时间复杂度 O(n22n) 级别。

扩展到两维,发现若先进行一次行操作再进行一次列操作,等价于将交点位置修改为整个矩阵的异或和。于是考虑扩展上述做法,题意等价于有一个大小为 (n+1)×(m+1) 的矩阵,第 n+1 行为各列的异或和,第 m+1 列为各行的异或和((n+1,m+1) 即整个矩阵异或和),每次操作可以交换两行/两列,求左上角 n×m 矩阵的美丽值的最小值。

考虑预处理任意两行/两列相邻时的贡献。发现两维的贡献是独立的,不同维的交换并不影响另一维的贡献。发现若枚举了哪一行被放在了 n+1 行上,则对列的贡献的计算就可以直接套用一维的做法了。于是考虑转移时处理 sum(i,j) 表示将第 i 行第 j 列时的最小美丽值,取最小值即可。

在一维做法的基础上仅需再多枚举一维即可,总时间复杂度 O(n2m2n+nm22m)O(n32n) 级别。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 16 + 2;
const int kS = (1 << 16) + 10;
const LL kInf = 1e18;
//=============================================================
int n, m, a[kN][kN];
LL ans, all, row[kN][kN], col[kN][kN], f[kS][kN];
LL sum[kN][kN];
//=============================================================
void init() {
a[n + 1][m + 1] = 0;
for (int i = 1; i <= n; ++ i) {
a[i][m + 1] = 0;
for (int j = 1; j <= m; ++ j) {
a[i][m + 1] ^= a[i][j];
a[n + 1][m + 1] ^= a[i][j];
}
}
for (int j = 1; j <= m; ++ j) {
a[n + 1][j] = 0;
for (int i = 1; i <= n; ++ i) {
a[n + 1][j] ^= a[i][j];
}
}
for (int i = 1; i <= n + 1; ++ i) {
for (int j = 1; j <= n + 1; ++ j) {
row[i][j] = 0;
for (int k = 1; k <= m + 1; ++ k)
row[i][j] += abs(a[i][k] - a[j][k]);
}
}
for (int i = 1; i <= m + 1; ++ i) {
for (int j = 1; j <= m + 1; ++ j) {
col[i][j] = 0;
for (int k = 1; k <= n + 1; ++ k)
col[i][j] += abs(a[k][i] - a[k][j]);
}
}
}
void DP() {
for (int i = 1; i <= n + 1; ++ i) {
for (int j = 1; j <= m + 1; ++ j) {
sum[i][j] = 0;
}
}
all = (1 << (n + 1));
for (int lst = 1; lst <= m + 1; ++ lst) {
for (int s = 1; s < all; ++ s) {
for (int i = 1; i <= n + 1; ++ i) {
f[s][i] = kInf;
}
}
for (int i = 1; i <= n + 1; ++ i) f[1 << (i - 1)][i] = 0;
for (int s = 1; s < all; ++ s) {
for (int i = 1; i <= n + 1; ++ i) {
if ((s >> (i - 1) & 1) == 0) continue;
for (int j = 1; j <= n + 1; ++ j) {
if (i == j || (s >> (j - 1) & 1)) continue;
f[s | (1 << (j - 1))][j] = std::min(f[s | (1 << (j - 1))][j],
f[s][i] + row[i][j] - abs(a[i][lst] - a[j][lst]));
}
}
}
for (int i = 1; i <= n + 1; ++ i) {
LL minf = kInf;
for (int j = 1; j <= n + 1; ++ j) {
if (i == j) continue;
minf = std::min(minf, f[(all - 1) ^ (1 << (i - 1))][j]);
}
sum[i][lst] += minf;
}
}
all = (1 << (m + 1));
for (int lst = 1; lst <= n + 1; ++ lst) {
for (int s = 1; s < all; ++ s) {
for (int i = 1; i <= m + 1; ++ i) {
f[s][i] = kInf;
}
}
for (int i = 1; i <= m + 1; ++ i) f[1 << (i - 1)][i] = 0;
for (int s = 1; s < all; ++ s) {
for (int i = 1; i <= m + 1; ++ i) {
if ((s >> (i - 1) & 1) == 0) continue;
for (int j = 1; j <= m + 1; ++ j) {
if (i == j || (s >> (j - 1) & 1)) continue;
f[s | (1 << (j - 1))][j] = std::min(f[s | (1 << (j - 1))][j],
f[s][i] + col[i][j] - abs(a[lst][i] - a[lst][j]));
}
}
}
for (int i = 1; i <= m + 1; ++ i) {
LL minf = kInf;
for (int j = 1; j <= m + 1; ++ j) {
if (i == j) continue;
minf = std::min(minf, f[(all - 1) ^ (1 << (i - 1))][j]);
}
sum[lst][i] += minf;
}
}
ans = kInf;
for (int i = 1; i <= n + 1; ++ i) {
for (int j = 1; j <= m + 1; ++ j) {
ans = std::min(ans, sum[i][j]);
}
}
}
//=============================================================
int main() {
// freopen("1.txt", "r", stdin);
std::ios::sync_with_stdio(0), std::cin.tie(0);
int T; std::cin >> T;
while (T --) {
std::cin >> n >> m;
for (int i = 1; i <= n; ++ i) {
for (int j = 1; j <= m; ++ j) {
std::cin >> a[i][j];
}
}
init();
DP();
std::cout << ans << "\n";
}
return 0;
}
/*
1
1 2
1 3
*/

F1

待补。

考虑镜像操作,将移动范围扩大到 2w×2h 的区域,并将区域平移到第一象限,则仅需考虑统计经过 (k1×2w,k2×2h) 多少次,k1,k2 任取。

写在最后

学到了什么:

  • D:考虑剩余系。

你说的对按照常理来说现在又是夹带私货环节:

结尾广告:中南大学 ACM 集训队绝赞招新中!

有信息奥赛基础,获得 NOIP 省一等奖并达到 Codeforces rating 1900+ 或同等水平及以上者,可以直接私聊我与校队队长联系,免选拔直接进校集训队参加区域赛!

没有达到该水平但有志于 XPCX 赛事请关注每学年开始的 ACM 校队招新喵!

到这个时候了还缺队友实在不妙!求求求求快来个大神带我呜呜呜呜

posted @   Luckyblock  阅读(229)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示