Codeforces Round 1033 (Div. 2) 个人题解(A-E)
A. Square of Rectangles
三个矩形拼成一个正方形有且只有两种情况:
- 三个矩形宽度相同并列
- 小的两个矩形宽度相同的并列,大的拼在一旁
分类讨论即可,注意讨论 \(l\) 和 \(b\) 谁作为长方形的宽
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
typedef unsigned long long ull;
typedef long long ll;
const int N = 2e5 + 5, M = 2e5 + 5, LGN = 20, mod = 998244353, B = 131, inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f, P = 1000000711;
void lxllxs() {
int l1, l2, l3, b1, b2, b3;
cin >> l1 >> b1 >> l2 >> b2 >> l3 >> b3;
swap(l1, l3);
swap(b1, b3);
if (l1 + l2 == l3 && b1 == b2 && b1 + b3 == l3) {
cout << "YES" << endl;
return;
}
if (b1 + b2 == b3 && l1 == l2 && l1 + l3 == b3) {
cout << "YES" << endl;
return;
}
if (b1 == b2 && b2 == b3 && l1 + l2 + l3 == b1) {
cout << "YES" << endl;
return;
}
if (l1 == l2 && l2 == l3 && b1 + b2 + b3 == l1) {
cout << "YES" << endl;
return;
}
cout << "NO" << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--)lxllxs();
return 0;
}
B. Square Pool
模拟样例可以发现,只有球在对角线上并且沿着对角线的方向才能进洞,判断条件写清楚即可
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
typedef unsigned long long ull;
typedef long long ll;
const int N = 2e5 + 5, M = 2e5 + 5, LGN = 20, mod = 998244353, B = 131, inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f, P = 1000000711;
void lxllxs() {
int n, s;
cin >> n >> s;
int res = 0;
while (n--) {
int dx, dy, x, y;
cin >> dx >> dy >> x >> y;
if (x + y == s && dx != dy)res++;
else if (x == y && dx == dy)res++;
}
cout << res << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--)lxllxs();
return 0;
}
C. Divine Tree
可以发现 \(1\leq d(i)\leq i\) ,所以 \(n\leq m\leq n*(n+1)/2\)
令所有的点 \(i\) 的父亲为 \(i+1\),此时所有的 \(d(i)\) 为 \(i\), 和为 \(n*(n+1)/2\)
构造方法:令所有 \(a[i]=d(i)\),从编号从大到小的顺序枚举,每次如果 \(a[i]\) 总和超过 \(m\) 就尽可能地减小 \(a[i]\) (至少为1),然后将点 \(i\) 的子边断掉,点 \(a[i]\) 作为其父节点。显然,此时的 \(a[i]\) 一定等于 \(d(i)\)。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
typedef unsigned long long ull;
typedef long long ll;
const int N = 2e5 + 5, M = 2e5 + 5, LGN = 20, mod = 998244353, B = 131, inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f, P = 1000000711;
void lxllxs() {
int n, m;
cin >> n >> m;
if (m < n || m > n * (n + 1) / 2) {
cout << -1 << endl;
return;
}
vector<int> ans(n + 1);
int sum = n * (n + 1) / 2;
for (int i = 1; i <= n; i++)ans[i] = i;
int t = -1;
for (int i = n; i >= 1; i--) {
if (sum > m) {
int p = min(sum - m, ans[i] - 1);
ans[i] -= p;
sum -= p;
} else {
t = i;
cout << t << endl;
break;
}
}
for (int i = 1; i + 1 <= t; i++) {
cout << i << " " << i + 1 << endl;
}
for (int i = t + 1; i <= n; i++) {
cout << i << " " << ans[i] << endl;
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--)lxllxs();
return 0;
}
D. Matrix game
每个位置会出现 \(1\)~\(k\) 的数。在一列中,如果必须要出现相同的 \(a\) 个数字,根据鸽巢原理,至少要 \(n=k\times (a-1)+1\) 行。
每一列都至少会出现一组相同的 \(a\) 个数字。我们就以最坏的情况每次都为 \(1\) 来看,把这 \(a\) 个数以外的数都看成 \(0\)(因为无关紧要),以每列为单位来看,这又是一个鸽巢原理的问题:每列可能出现 \(k\times C_n^a\) 个向量,如果必须要出现相同的 \(b\) 个向量,至少要 \(m=k\times C_n^a\times (b-1)+1\) 列。
求组合数这里,虽然 \(n\) 很大,但 \(a\) 在可枚举范围内,所以直接枚举计算就行了。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
typedef unsigned long long ull;
typedef long long ll;
const int N = 2e5 + 5, M = 2e5 + 5, LGN = 20, mod = 1e9 + 7, B = 131, inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f, P = 1000000711;
typedef __int128 i128;
int qmi(int a, int p) {
int res = 1;
while (p) {
if (p & 1)res = res * a % mod;
a = a * a % mod;
p >>= 1;
}
return res;
}
void lxllxs() {
int a, b, k;
cin >> a >> b >> k;
int ans1 = ((a - 1) * k + 1) % mod;
int ans2 = 1;
for (int i = 0, j = (a - 1) * k + 1; i < a; i++, j--) {
ans2 = (i128) ans2 * j % mod;
}
for (int i = 0; i < a; i++) {
ans2 = ans2 * qmi(i + 1, mod - 2) % mod;
}
ans2 = (ans2 * (b - 1) % mod * k % mod + 1) % mod;
cout << ans1 << " " << ans2 << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--)lxllxs();
return 0;
}
E. Lanes of Cars
总的愤怒值为 \(\sum \frac{a_i \times (a_i-1)}{2}+cnt\times k\),\(cnt\) 为切换车道的总次数
有一个很直接的贪心方式就是当 \(max\{a_i\}-min\{a_i\}>k+1\) 时,然后最大值减一,最小值加一。但是如果一次一次地操作,在 \(a_i\) 的范围下,肯定会超时。所以考虑通过二分边界(第一次),进行批量操作:二分操作完后的最大值+1的数。
注:二分只是找到一个好处理的边界,不是所有操作的边界,所以二分的边界操作完后,还会有余量操作
二分完后可以得到操作的次数,然后如何将这些 \(1\) 一个一个全部加到最小值上? 同样的可以进行二分边界(第二次),批量操作:二分把这些 \(1\) 加上去后的最小值-1的数。二分后(第二次)会有几个余量的操作,可以直接枚举完成。
然后第一次二分得到的操作解决完后,也会有一些余量的操作,这部分可以用 \(multiset\) 暴力。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
typedef unsigned long long ull;
typedef long long ll;
const int N = 2e5 + 5, M = 2e5 + 5, LGN = 20, mod = 1e9 + 7, B = 131, inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f, P = 1000000711;
void lxllxs() {
int n, k;
cin >> n >> k;
vector<int> a(n);
for (int i = 0; i < n; i++)cin >> a[i];
sort(a.begin(), a.end());
auto check1 = [&](int mid) {
int ct1 = 0, ct0 = 0;
for (int i = 0; i < n; i++) {
if (a[i] > mid)ct1 += a[i] - mid;
if (a[i] < mid - k)ct0 += mid - k - a[i];
}
return ct1 <= ct0;
};
int l = 1, r = 1e6;
while (l < r) {
int mid = l + r >> 1;
if (check1(mid))r = mid;
else l = mid + 1;
}
int res = 0, cnt = 0;
for (int i = 0; i < n; i++) {
if (a[i] > l) {
res += (a[i] - l) * k;
cnt += a[i] - l;
a[i] = l;
}
}
auto check2 = [&](int mid) {
int ct = 0;
for (int i = 0; i < n; i++) {
if (a[i] < mid)ct += mid - a[i];
}
return ct <= cnt;
};
l = 1, r = 1e6;
while (l < r) {
int mid = l + r + 1 >> 1;
if (check2(mid))l = mid;
else r = mid - 1;
}
for (int i = 0; i < n; i++) {
if (a[i] < l) {
cnt -= l - a[i];
a[i] = l;
}
}
for (int i = 0; i < cnt; i++)a[i]++;
multiset<int> se;
for (int i = 0; i < n; i++) {
se.insert(a[i]);
}
while (true) {
auto p1 = *se.begin(), p2 = *se.rbegin();
if (p2 - p1 > k + 1) {
se.erase(se.find(p1));
se.erase(se.find(p2));
se.insert(p1 + 1);
se.insert(p2 - 1);
res += k;
} else break;
}
for (auto x: se) {
res += x * (x + 1) / 2;
}
cout << res << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--)lxllxs();
return 0;
}

浙公网安备 33010602011771号