2023山东省赛vp&补题记录
摘要
临时组了个队伍打vp,最终结果是4题682罚时。稳了个铜。
这个结果并不是很满意,可能是最近思维练得少了,感觉脑子有点转不过弯。
无意义的提交也有好多,感觉正常水平至少应该是5题的。
省赛在即,继续加油叭~
赛时部分
A-Orders
排序
将所有订单根据 \(a_i\) 排序,维护一个总数 \(sum\) ,在遍历的过程中每次将 \(sum\) 加上 \((a_i-a_{i-1})\ast k\) 再减去 \(b_i\) 即可。当某个时刻 \(sum\) 小于 \(0\) 时则直接输出 \(No\) 并结束程序。
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
#define ull unsigned long long
#define pii pair<int, int>
#define pll pair<ll, ll>
#define endl '\n'
#define Y cout << "Yes\n"
#define N cout << "No\n"
#define fi first
#define se second
const int MOD = 1e9+7, inf = 0x3f3f3f3f;
void solve() {
int n, k, a, b;
cin >> n >> k;
map<int, int> need;
for(int i = 0; i < n; i ++ ) {
cin >> a >> b;
need[a] += b;
}
ll sum = 0, last = 0;
for(auto &[a, b] : need) {
sum += (a-last)*k;
sum -= b;
if(sum < 0) {
N;
return;
}
last = a;
}
Y;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while(t--) solve();
return 0;
}
I - Three Dice
枚举
枚举每一种情况与测试数据进行比较即可。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define pii pair<int, int>
#define pll pair<ll, ll>
#define endl '\n'
#define Y cout << "Yes\n"
#define N cout << "No\n"
#define fi first
#define se second
const int MOD = 1e9+7, inf = 0x3f3f3f3f;
void solve() {
int x, y;
cin >> x >> y;
int dx[7] = {0, 1, 0, 0, 4, 0, 0};
int dy[7] = {0, 0, 2, 3, 0, 5, 6};
for(int i = 1; i <= 6; i ++ ) {
for(int j = 1; j <= 6; j ++ ) {
for(int k = 1; k <= 6; k ++ ) {
if(dx[i]+dx[j]+dx[k] == x && dy[i]+dy[j]+dy[k] == y) {
Y;
return;
}
}
}
}
N;
return;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int t = 1;
//cin >> t;
while(t--) solve();
return 0;
}
G - Matching
离散化, 排序
对条件移项得 \(i-a_i = j-a_j\) 因此我们只需要维护一个点值与下标差值的表,将差值相同的点放在同一个点集中。然后枚举每一个差值,将其中值最大的两个点建边,如果边权为正则加入答案中。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define pii pair<int, int>
#define pll pair<ll, ll>
#define endl '\n'
#define Y cout << "YES\n"
#define N cout << "NO\n"
#define fi first
#define se second
const int MOD = 1e9+7, inf = 0x3f3f3f3f;
void solve() {
int n;
cin >> n;
vector<int> a(n+1);
map<int, vector<int>> cnt;
for(int i = 1; i <= n; i ++ ) {
cin >> a[i];
cnt[i-a[i]].push_back(a[i]);
}
ll ans = 0;
for(auto &[a, b] : cnt) {
if(b.size() == 1) continue;
sort(b.begin(), b.end(), greater());
if(b[0] <= 0) continue;
ll sum = 0;
for(int i = 0; i < b.size(); i += 2) {
if(b[i]+b[i+1] > 0 && i+1 < b.size()) sum += b[i]+b[i+1];
else break;
}
ans += sum;
}
cout << ans << endl;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while(t--) solve();
return 0;
}
L - Puzzle: Sashigane
模拟
因为给出的网格为 \(n \ast n\) 大小,而我们容易得知,对于每一个正方形 \(n \ast n\) 都可以通过填充一个“L”形,从而得到 \((n+1) \ast (n+1)\) ,因此对于任何数据我们的答案都是 \(Yes\) ,而根据这个规律,我们不难得出,从 \(1 \ast 1\) 的正方形扩展到 \(n \ast n\) 的正方形需要填充 \(n-1\) 个“L”形。
同时,我们在填充的时候,可以采取先将初始位置方块所在的角落填满,然后再沿着一个对角线方向填充至 \(n \ast n\)。那么我们在一开始填充角落的时候采取的策略就应该是先往靠近边界的方向填充。
有了上面的思路,我们可以维护正方形的四个顶点,当四个定点不与 \(n \ast n\) 的正方形顶点重合时,不断进行模拟即可。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define pii pair<int, int>
#define pll pair<ll, ll>
#define endl '\n'
#define Y cout << "Yes\n"
#define N cout << "No\n"
#define fi first
#define se second
const int MOD = 1e9+7, inf = 0x3f3f3f3f;
int n, x, y;
int cnt = 1;
int get_dir(pii leftdown, pii rightup) {
bool right = 0, left = 0, up = 0, down = 0;
int x1 = leftdown.fi, y1 = leftdown.se, x2 = rightup.fi, y2 = rightup.se;
if((n-x1 <= x2-1 && n-x1 != 0) || x2 == 1) down = 1;
else if((n-x1 > x2-1 && x2-1 != 0) || x1 == n) up = 1;
if((n-y2 <= y1-1 && n-y2 != 0) || y1 == 1) right = 1;
else if((y1-1 <= n-y2 && y1-1 != 0) || y2 == n) left = 1;
if(left && up) return 0;
else if(right && up) return 1;
else if(right && down) return 2;
else if(left && down) return 3;
return 4;
}
void solve() {
cin >> n >> x >> y;
pii leftdown, leftup, rightdown, rightup;
leftdown = leftup = rightdown = rightup = {x, y};
int len = 1;
Y;
cout << n-1 << endl;
while((leftdown.fi != n || leftdown.se != 1) || (leftup.fi != 1 || leftup.se != 1) || (rightdown.fi != n || rightdown.se != n) || (rightup.fi != 1 || rightup.se != n)) {
int dir = get_dir(leftdown, rightup);
if(dir == 0) {
cout << leftup.fi-1 << ' ' << leftup.se-1 << ' ';
cout << len << ' ' << len << endl;
leftup.fi--, leftup.se--;
leftdown.se--, rightup.fi--;
} else if(dir == 1) {
cout << rightup.fi-1 << ' ' << rightup.se+1 << ' ';
cout << len << ' ' << -len << endl;
rightup.fi--, rightup.se++;
leftup.fi--, rightdown.se++;
} else if(dir == 2) {
cout << rightdown.fi+1 << ' ' << rightdown.se+1 << ' ';
cout << -len << ' ' << -len << endl;
rightdown.fi++, rightdown.se++;
leftdown.fi++, rightup.se++;
} else if(dir == 3){
cout << leftdown.fi+1 << ' ' << leftdown.se-1 << ' ';
cout << -len << ' ' << len << endl;
leftdown.fi++, leftdown.se--;
leftup.se--, rightdown.fi++;
}
len++;
}
return;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int t = 1;
//cin >> t;
while(t--) solve();
return 0;
}
补题部分
D - Fast and Fat
二分答案,贪心
由题目公式可以推出,当答案为 \(x\) 时,速度大于 \(x\) 的队员最多可以背起体重 \(w_j = v_i+w_i-x\) 的队员。
根据这个公式我们可以发现,当 \(x\) 越小时,队员可以背起的体重就越大,也就是说对于最终答案 \(x_{max}\) 我们有任意 \(x< x_{max}\) 符合要求。到此,我们已经发现答案具有单调性,考虑使用二分答案法解决。
那么如何判断答案是否符合要求呢?我们将速度大于等于答案 \(x\) 的队员分为背负组,将小于 \(x\) 的队员分为被背负组,那么对于被背负组中体重最大的队员,我们一定是选择背负组中可以背负的体重最大的队员进行配队为最优解,因此只需要对两组数据进行降序排序,一次枚举当前两组的最大值是否符合要求即可。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define pii pair<int, int>
#define pll pair<ll, ll>
#define endl '\n'
#define Y cout << "YES\n"
#define N cout << "NO\n"
#define fi first
#define se second
const int MOD = 1e9+7, inf = 0x3f3f3f3f;
int n;
bool check(ll mid, vector<pll>& A, vector<pll>& B) {
vector<ll> p, q;
for(int i = 0; i < n; i ++ ) {
if(A[i].fi >= mid) p.push_back(A[i].fi+A[i].se-mid);
}
for(int i = 0; i < n; i ++ ) {
if(B[i].first < mid) q.push_back(B[i].se);
}
if(p.size() < q.size()) return false;
for(int i = 0; i < q.size(); i ++ ) if(p[i] < q[i]) return false;
return true;
}
void solve() {
cin >> n;
vector<pll> A(n), B(n);
for(int i = 0; i < n; i ++ ) {
int v, w;
cin >> v >> w;
A[i] = B[i] = pll(v, w);
}
sort(A.begin(), A.end(), [](pll &a, pll &b) {
return a.fi + a.se > b.fi + b.se;
});
sort(B.begin(), B.end(), [](pll &a, pll &b){
return a.se > b.se;
});
ll l = A[0].fi, r = A[0].fi;
for(int i = 1; i < n; i ++ ) {
l = min(l, A[i].fi), r = max(r, A[i].fi);
}
while(l < r) {
ll mid = l+r+1 >> 1;
if(check(mid, A, B)) l = mid;
else r = mid-1;
}
cout << l << endl;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while(t--) solve();
return 0;
}