Codeforces Round 530 (Div. 2)
写在前面
比赛地址:https://codeforces.com/contest/1099。
睡不着又不知道玩什么随便开了把。
19 年的怎么这么水感觉和最近的难度差好多呃呃
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 w, h; std::cin >> w >> h;
int u1, d1, u2, d2; std::cin >> u1 >> d1 >> u2 >> d2;
LL s = w;
for (int i = h; i >= 0; -- i) {
s += i;
if (i == d1) s = std::max(0ll, s - u1);
if (i == d2) s = std::max(0ll, s - u2);
}
std::cout << s << "\n";
return 0;
}
B
签到。
周长相等的矩形中正方形的面积最大,确定了矩形的形态后仅需将画出一组长和高即可,答案即 \(w+h\)。
令 \(s = \sqrt{n}\),显然 \(s\times s\) 的矩形一定合法,检查 \((s-1)\times s\) 的矩形是否合法即可。
//
/*
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 n; std::cin >> n;
int s = ceil(sqrt(n));
if (s * (s - 1) >= n) {
std::cout << s + s - 1 << "\n";
} else {
std::cout << 2 * s << "\n";
}
return 0;
}
C
签到。
若 \(k>n\) 则字符串中至少有一个 *
,若 \(k<n\) 则特殊字符数不小于 \(n-k\)。
模拟即可。
//
/*
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);
std::string s; std::cin >> s;
int k; std::cin >> k;
int cntp1 = 0, cntp2 = 0;
for (int i = 0, len = s.length(); i < len; ++ i) {
if (s[i] == '?') ++ cntp1;
if (s[i] == '*') ++ cntp2;
}
int true_len = s.length() - cntp1 - cntp2;
if (k > true_len && !cntp2) {
std::cout << "Impossible";
return 0;
} else if (k < true_len && true_len - k > cntp1 + cntp2) {
std::cout << "Impossible";
return 0;
}
std::string ans;
if (k > true_len) {
for (int i = 0, len = s.length(); i < len; ++ i) {
if (s[i] == '*') {
while (true_len < k) ans.push_back(s[i - 1]), ++ true_len;
} else if (s[i] != '?') {
ans.push_back(s[i]);
}
}
} else {
for (int i = 0, len = s.length(); i < len; ++ i) {
if (s[i] == '?' || s[i] == '*') {
if (true_len > k) ans.pop_back(), -- true_len;
} else {
ans.push_back(s[i]);
}
}
}
std::cout << ans << "\n";
return 0;
}
D
构造。
若树不合法说明每种满足给定条件的构造,均会使某节点的权值变为负数。
为了在给定的 \(s\) 的限制下最小化 \(\sum_u a_u\),一个显然的想法是令分叉点 \(u\) 的 \(a_u\) 尽可能大,从而更多地在其子节点的 \(s_v\) 中复用 \(a_u\) 的贡献。则对于所有未知权值的节点 \(u\),在保证 \(a_u\ge 0\) 时最优的构造是:
DFS 构造出 \(s\) 后再差分检查 \(a\) 是否非负即可。
//构造
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 1e5 + 10;
const int kInf = 1e9 + 2077;
//=============================================================
int n, fa[kN], s[kN];
int edgenum, head[kN], v[kN << 1], ne[kN << 1];
bool flag = 1;
LL ans;
//=============================================================
void Add(int u_, int v_) {
v[++ edgenum] = v_;
ne[edgenum] = head[u_];
head[u_] = edgenum;
}
void Dfs1(int u_, int fa_) {
int minn = kInf;
for (int i = head[u_]; i; i = ne[i]) {
Dfs1(v[i], u_);
minn = std::min(minn, s[v[i]]);
}
if (s[u_] == -1) s[u_] = ((minn == kInf) ? s[fa_] : minn);
}
void Dfs2(int u_, int fa_) {
ans += s[u_] - s[fa_];
if (s[u_] < s[fa_]) flag = 0;
for (int i = head[u_]; i; i = ne[i]) Dfs2(v[i], u_);
}
//=============================================================
int main() {
//freopen("1.txt", "r", stdin);
std::ios::sync_with_stdio(0), std::cin.tie(0);
std::cin >> n;
for (int i = 2; i <= n; ++ i) {
int f; std::cin >> f;
Add(f, i);
}
for (int i = 1; i <= n; ++ i) std::cin >> s[i];
Dfs1(1, 0), Dfs2(1, 0);
if (!flag) ans = -1;
std::cout << ans << "\n";
return 0;
}
E
结论,码农
基于小结论的呃呃码农提。
发现对于所有的合法矩阵,只有两种情况:
- 每行均只有两种字符,奇偶行上的字符种类互补,且每行的形态只有
ABABABAB
或BABABABA
两种形态,且各行独立。 - 每列均只有两种字符,奇偶列上的字符种类互补,且每列的形态只有
ABABABAB
或BABABABA
两种形态,且各列独立。
于是先考虑第一种情况,枚举字符种类后按行枚举找各行的最少修改数贪心修改,然后转下矩阵重复即可。
妈的码农
//结论,码农
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
char ch[5] = "AGCT";
int n, m, type, minsum;
std::string seed;
std::vector <std::string> a, ans;
//=============================================================
void Init() {
std::cin >> n >> m;
minsum = n * m;
for (int i = 0; i < n; ++ i) {
std::string s; std::cin >> s;
a.push_back(s);
}
}
void checka(std::string s_) {
for (int i = 0; i < 4; ++ i) {
for (int j = 0; j < i; ++ j) {
if (s_[i] == s_[j]) return ;
}
}
int sum = 0;
for (int i = 0; i < n; ++ i) {
int s1 = 0, s2 = 0;
std::string t1 = s_.substr(2 * (i % 2 == 1), 2);
std::string t2 = t1.substr(1, 1) + t1.substr(0, 1);
for (int j = 0; j < m; j += 2) {
for (int k = j, l = 0; k <= std::min(j + 1, m - 1); ++ k, ++ l) {
if (a[i][k] != t1[l]) ++ s1;
if (a[i][k] != t2[l]) ++ s2;
}
}
sum += std::min(s1, s2);
}
if (sum < minsum) type = 0, minsum = sum, seed = s_;
}
void checkb(std::string s_) {
for (int i = 0; i < 4; ++ i) {
for (int j = 0; j < i; ++ j) {
if (s_[i] == s_[j]) return ;
}
}
int sum = 0;
for (int i = 0; i < m; ++ i) {
int s1 = 0, s2 = 0;
std::string t1 = s_.substr(2 * (i % 2 == 1), 2);
std::string t2 = t1.substr(1, 1) + t1.substr(0, 1);
for (int j = 0; j < n; j += 2) {
for (int k = j, l = 0; k <= std::min(j + 1, n - 1); ++ k, ++ l) {
if (a[k][i] != t1[l]) ++ s1;
if (a[k][i] != t2[l]) ++ s2;
}
}
sum += std::min(s1, s2);
}
if (sum < minsum) type = 1, minsum = sum, seed = s_;
}
void getansa() {
for (int i = 0; i < n; ++ i) {
int s1 = 0, s2 = 0;
std::string t1 = seed.substr(2 * (i % 2 == 1), 2);
std::string t2 = t1.substr(1, 1) + t1.substr(0, 1);
for (int j = 0; j < m; j += 2) {
for (int k = j, l = 0; k <= std::min(j + 1, m - 1); ++ k, ++ l) {
if (a[i][k] != t1[l]) ++ s1;
if (a[i][k] != t2[l]) ++ s2;
}
}
ans.push_back("");
if (s1 <= s2) {
while ((int) ans[i].length() + 2 <= m) ans[i] += t1;
if ((int) ans[i].length() < m) ans[i].push_back(t1[0]);
} else {
while ((int) ans[i].length() + 2 <= m) ans[i] += t2;
if ((int) ans[i].length() < m) ans[i].push_back(t2[0]);
}
}
}
void getansb() {
for (int i = 0; i < n; ++ i) ans.push_back("");
for (int i = 0; i < m; ++ i) {
int s1 = 0, s2 = 0;
std::string t1 = seed.substr(2 * (i % 2 == 1), 2);
std::string t2 = t1.substr(1, 1) + t1.substr(0, 1);
for (int j = 0; j < n; j += 2) {
for (int k = j, l = 0; k <= std::min(j + 1, n - 1); ++ k, ++ l) {
if (a[k][i] != t1[l]) ++ s1;
if (a[k][i] != t2[l]) ++ s2;
}
}
int len = 0;
if (s1 <= s2) {
for (; len + 2 <= n; len += 2) {
ans[len].push_back(t1[0]);
ans[len + 1].push_back(t1[1]);
}
if (len < n) ans[len].push_back(t1[0]);
} else {
for (; len + 2 <= n; len += 2) {
ans[len].push_back(t2[0]);
ans[len + 1].push_back(t2[1]);
}
if (len < n) ans[len].push_back(t2[0]);
}
}
}
//=============================================================
int main() {
// freopen("1.txt", "r", stdin);
std::ios::sync_with_stdio(0), std::cin.tie(0);
Init();
for (int i = 0; i < 4; ++ i) {
for (int j = 0; j < 4; ++ j) {
for (int k = 0; k < 4; ++ k) {
for (int l = 0; l < 4; ++ l) {
std::string s;
s.push_back(ch[i]), s.push_back(ch[j]);
s.push_back(ch[k]), s.push_back(ch[l]);
checka(s), checkb(s);
}
}
}
}
type ? getansb() : getansa();
for (auto s: ans) std::cout << s << "\n";
return 0;
}
F
线段树,博弈,DP
首先考虑二分答案,检查在至少吃 \(\operatorname{lim}\) 个饼☆下能否到达一个必胜节点即可。节点 \(u\) 是获胜节点当且仅当满:
- 在 \(T - 2\times \operatorname{dis}(1, u)\) 时间内,按照所需时间升序吃路径 \(1\rightarrow u\) 上的饼干可以吃到至少 \(\operatorname{lim}\) 个。考虑对路径 \(1\rightarrow u\) 上所有饼干维护权值线段树,下标为吃完饼干所需时间,维护区间饼干数量与吃完区间所有饼干所需时间,线段树上二分即可。
- 不满足条件一,且 \(u\not= 1\),则子节点中应至少有两个必胜节点,使得在删掉一个子节点后仍可获胜。
- 不满足条件一二,且 \(u=1\),则子节点中应至少有一个必胜节点。
于是在 DFS 的过程中对路径 \(1\rightarrow u\) 上所有饼干维护权值线段树,回溯时更新该节点是否为必胜节点即可。
若节点 1 为必胜节点则返回 True
。
总时间复杂度 \(O(n\log^2 \left(\max t\right))\)。
//线段树,博弈,DP
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 1e5 + 10;
//=============================================================
int n;
int edgenum, head[kN], v[kN], w[kN], ne[kN];
int cnt[kN], maxt, tim[kN];
bool yes[kN];
LL T, sumc, ans;
//=============================================================
namespace Seg {
#define ls (now_<<1)
#define rs (now_<<1|1)
#define mid ((L_+R_)>>1)
const int kNode = 4e6 + 10;
LL sum[kNode], cnt[kNode];
void Pushup(int now_) {
sum[now_] = sum[ls] + sum[rs];
cnt[now_] = cnt[ls] + cnt[rs];
}
void Insert(int now_, int L_, int R_, int pos_, LL sum_, LL cnt_) {
if (L_ == R_) {
sum[now_] += sum_;
cnt[now_] += cnt_;
return ;
}
if (pos_ <= mid) Insert(ls, L_, mid, pos_, sum_, cnt_);
else Insert(rs, mid + 1, R_, pos_, sum_, cnt_);
Pushup(now_);
}
int Query(int now_, int L_, int R_, LL lim_) {
if (L_ == R_) {
return std::min(cnt[now_], lim_ / L_);
}
LL sl = sum[ls], cl = cnt[ls];
if (lim_ <= sl) return Query(ls, L_, mid, lim_);
return cl + Query(rs, mid + 1, R_, lim_ - sl);
}
#undef ls
#undef rs
#undef mid
}
void Add(int u_, int v_, int w_) {
v[++ edgenum] = v_;
w[edgenum] = w_;
ne[edgenum] = head[u_];
head[u_] = edgenum;
}
void Init() {
std::cin >> n >> T;
for (int i = 1; i <= n; ++ i) {
std::cin >> cnt[i];
sumc += cnt[i];
}
for (int i = 1; i <= n; ++ i) {
std::cin >> tim[i];
maxt = std::max(maxt, tim[i]);
}
for (int i = 2; i <= n; ++ i) {
int u_, w_; std::cin >> u_ >> w_;
Add(u_, i, w_);
}
}
void Dfs(int u_, LL dis_, LL lim_) {
if (dis_ > T) return ;
Seg::Insert(1, 1, maxt, tim[u_], 1ll * tim[u_] * cnt[u_], cnt[u_]);
int yesc = 0;
for (int i = head[u_]; i; i = ne[i]) {
Dfs(v[i], dis_ + w[i], lim_);
yesc += yes[v[i]];
}
yes[u_] = (Seg::Query(1, 1, maxt, T - 2ll * dis_) >= lim_) || (yesc >= 2);
if (u_ == 1) yes[u_] = yes[u_] || (yesc >= 1);
Seg::Insert(1, 1, maxt, tim[u_], -1ll * tim[u_] * cnt[u_], -cnt[u_]);
}
bool check(LL lim_) {
memset(yes, 0, (n + 1) * sizeof (bool));
Dfs(1, 0, lim_);
return yes[1];
}
//=============================================================
int main() {
// freopen("1.txt", "r", stdin);
std::ios::sync_with_stdio(0), std::cin.tie(0);
Init();
for (LL l = 0, r = sumc; l <= r; ) {
LL mid = ((l + r) >> 1ll);
if (check(mid)) {
ans = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
std::cout << ans << "\n";
return 0;
}
写在最后
学到了什么:
- E:我草我不想写码农提
- F:必胜点的 DP