(2024)第五届辽宁省大学生程序设计竞赛 (大连)
A. 爱上字典
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
using ui32 = unsigned int;
using vi = vector<int>;
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
string s, t = "";
getline(cin, s);
s += ' ';
set<string> cnt;
for(int i = 0; i < s.size(); i ++) {
if(s[i] >= 'a' and s[i] <= 'z')
t += s[i];
else if(s[i] >= 'A' and s[i] <= 'Z')
t += s[i] - 'A' + 'a';
else {
if(t.empty()) continue;
cnt.insert(t);
t = "";
}
}
int n;
cin >> n;
for(; n; n --) {
cin >> s;
for(auto &c : s){
if(c >= 'A' and c <= 'Z') c = c - 'A' + 'a';
}
cnt.erase(s);
}
cout << cnt.size() << "\n";
return 0;
}
B. 比分幻术
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
using ui32 = unsigned int;
using vi = vector<int>;
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
string s;
cin >> s;
swap(s[0], s[2]);
cout << s;
return 0;
}
C. 插排串联
首先可以用dfs,求出所有节点的功率,然后贪心的给功率大的节点分配大的功率即可。
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using ui32 = unsigned int;
#define int i64
using vi = vector<int>;
using pii = pair<int,int>;
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int n;
cin >> n;
vi W(n + 1);
W[0] = -1;
vi b, node;
vector<vi> e(n + 1);
for(int i = 1, f; i <= n; i ++) {
cin >> f >> W[i];
e[f].push_back(i);
if(W[f] != -1)
b.push_back(W[f]), W[f] = -1, node.push_back(f);
}
auto dfs = [&](auto self, int x) -> void {
if(W[x] != -1) return;
W[x] = 0;
for(auto y : e[x]) {
self(self, y);
W[x] += W[y];
}
return;
};
dfs(dfs, 0);
if(W[0] > 2200) {
cout << "NO\n";
return 0;
}
ranges::sort(b);
ranges::sort(node ,[&](const int &x, const int &y) -> bool {
return W[x] < W[y];
});
for(int i = 0; i < b.size(); i ++) {
if(b[i] >= W[node[i]]) continue;
cout << "NO\n";
return 0;
}
cout << "YES\n";
return 0;
}
D. 都市叠高
看似求凸包,直径一定是一组点对,因此直接 dp 就好。
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using ui32 = unsigned int;
using ldb = long double;
#define int i64
using vi = vector<int>;
using pii = pair<int,int>;
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int n;
cin >> n;
vector<ldb> x(n + 1), y(n + 1);
for(int i = 1, _x, _y; i <= n; i++)
cin >> _x >> _y, x[i] = _x, y[i] = _y;
vector<ldb> f(n + 1);
for(int i = 1; i <= n; i ++) {
f[i] = f[i - 1];
ldb maxx = 0;
for(int j = i - 1; j >= 1; j --){
maxx = max(maxx, hypot(x[i] - x[j], y[i] - y[j]));
f[i] = max(f[i], f[j - 1] + maxx);
}
}
cout << fixed << setprecision(20) << f[n] << "\n";
return 0;
}
E. 俄式简餐
能够拼出来的基础方形有\(1\times 4,2\times 6\),两种。
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
using ui32 = unsigned int;
using vi = vector<int>;
void solve(){
int n, m, k = 0;
cin >> n >> m;
if((n * m) % 4 != 0) {
cout << "NO\n";
return;
}
if(n == 2 and m == 2) {
cout << "NO\n";
return;
}
vector g(n + 1, vi(m + 1));
if(n % 4 == 0){
for(int j = 1; j <= m; j ++)
for(int i = 1; i <= n; i += 4) {
k ++;
g[i][j] = g[i + 1][j] = g[i + 2][j] = g[i + 3][j] = k;
}
} else if(m % 4 == 0) {
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= m; j += 4) {
k ++;
g[i][j] = g[i][j + 1] = g[i][j + 2] = g[i][j + 3] = k;
}
} else if(m > 2) {
for(int i = 1; i <= n; i += 2) {
k ++;
g[i][1] = g[i][2] = g[i][3] = g[i + 1][1] = k;
k ++;
g[i][4] = g[i][5] = g[i][6] = g[i + 1][6] = k;
k ++;
g[i + 1][2] = g[i + 1][3] = g[i + 1][4] = g[i + 1][5] = k;
for(int j = 7; j <= m; j += 4) {
k ++;
g[i][j] = g[i][j + 1] = g[i][j + 2] = g[i][j + 3] = k;
k ++;
g[i + 1][j] = g[i + 1][j + 1] = g[i + 1][j + 2] = g[i + 1][j + 3] = k;
}
}
} else if(n > 2){
for(int j = 1; j <= m; j += 2) {
k ++;
g[1][j] = g[2][j] = g[3][j] = g[1][j + 1] = k;
k ++;
g[4][j] = g[5][j] = g[6][j] = g[6][j + 1] = k;
k ++;
g[2][j + 1] = g[3][j + 1] = g[4][j + 1] = g[5][j + 1] = k;
for(int i = 7; i <= n; i += 4) {
k ++;
g[i][j] = g[i + 1][j] = g[i + 2][j] = g[i + 3][j] = k;
k ++;
g[i][j + 1] = g[i + 1][j + 1] = g[i + 2][j + 1] = g[i + 3][j + 1] = k;
}
}
}
cout << "YES\n";
for(int i = 1; i <= n; i ++){
for(int j = 1; j <= m; j ++) {
cout << g[i][j] << " ";
}
cout << "\n";
}
return ;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t;
std::cin >> t;
while (t--){
solve();
}
return 0;
}
G. 顾影自怜
我们可以枚举每一个数\(a_i\),作为第一个最大值出现。为了计算答案,我们要预处理\(L[i]\)表示\(i\)左侧第一个比\(a_i\)大的数的下标,\(R[i]\)表示\(i\)右侧第一个比\(a_i\)大的数的下标。\(lst[i]\)表示\(i\)第一个\(a_i\)出现的下标,\(nxt[i]\)表示\(i\)后第\(k\)个\(a_i\)出现的位置。
则\(a_i\)作为第一个最大值的合法左端点的范围是\((\min(lst[i], L[i]), i]\),右端点的范围是\([nxt[i],R[i])\)。
\(L[i],R[i]\)用单调栈统计,\(lst[i],nxt[i]\)用值域桶统计,总体复杂度\(O(N)\)
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using ui32 = unsigned int;
#define int i64
using vi = vector<int>;
void solve() {
int n, k;
cin >> n >> k;
vi a(n + 1);
for(int i = 1; i <= n; i ++)
cin >> a[i];
for(int i = 1; i <= n; i ++)
assert( 1 <= a[i] and a[i] <= n);
vector vis(n + 1, vi(1, 0));
for(int i = 1;i <= n; i ++)
vis[a[i]].push_back(i);
vi lst(n + 1), nxt(n + 1, -1);
for(int i = 1; i <= n; i ++) {
if(vis[i].size() - 1 < k) continue;
for(int l = 1, r = k; r < vis[i].size(); l ++, r ++) {
lst[vis[i][l]] = vis[i][l - 1];
nxt[vis[i][l]] = vis[i][r];
}
}
vi L(n + 1);
stack<int> stk;
for(int i = 1; i <= n; i ++) {
while(not stk.empty() and a[stk.top()] <= a[i])
stk.pop();
if(stk.empty()) L[i] = 0;
else L[i] = stk.top();
stk.push(i);
}
vi R(n + 1);
stk = stack<int>();
for(int i = n; i >= 1; i --) {
while(not stk.empty() and a[stk.top()] <= a[i])
stk.pop();
if(stk.empty()) R[i] = n + 1;
else R[i] = stk.top();
stk.push(i);
}
int res = 0;
for(int i = 1; i <= n; i ++){
if(nxt[i] == -1) continue;
int l = i - max(lst[i], L[i]);
int r = R[i] - nxt[i];
if(l < 0 or r < 0) continue;
res += l * r;
}
cout << res << "\n";
return ;
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int T;
cin >> T;
while(T --)
solve();
return 0;
}
J. 结课风云
签到
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
using ui32 = unsigned int;
using vi = vector<int>;
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int n;
cin >> n;
int a, b, c;
cin >> a >> b >> c;
vi x(n), y(n);
for(int i = 0 ; i < n; i ++)
cin >> x[i] >> y[i];
int ret = 0;
for(int i = 0; i < n; i ++)
ret += (x[i] + y[i]) < c;
int d;
cin >> d;
for(int i = 0; i < n; i ++)
x[i] = min(x[i] + d, a);
int res = 0;
for(int i = 0; i < n; i ++)
res += (x[i] + y[i]) < c;
cout << ret - res;
return 0;
}
L. 龙之研习
可以发现,不会有任何一个数同时满足两个及以上条件。因此就变成了简单的容斥。
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using ui32 = unsigned int;
#define int i64
using vi = vector<int>;
int base = 0;
vi p1, p2;
void init() {
p1.push_back(4), p2.push_back(100);
while(p2.back() != 1e18) {
p1.push_back(p1.back() * 100);
p2.push_back(p2.back() * 100);
}
}
int calc(int x) {
int y = x;
for(int i = 0; i < p1.size(); i ++)
y = y - (x / p1[i]) + (x / p2[i]);
return y;
}
void solve() {
int k;
cin >> k;
int l = 1, r = 2e18, ret = -1;
while(l <= r) {
int mid = (l + r) / 2;
int cnt = calc(mid);
if(cnt - base >= k) ret = mid, r = mid - 1;
else l = mid + 1;
}
cout << ret << "\n";
return;
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
init();
base = calc(2024);
int T;
cin >> T;
while(T --)
solve();
return 0;
}
M. 盲盒谜题
模拟题,这里把放和删操作做了一个封装。
有一个注意点是,只要被放了,最后就一定会拿到,因此可以在放的过程中直接统计拿了多少。
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
using ui32 = unsigned int;
using vi = vector<int>;
using pii = pair<int,int>;
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int have, m, k, lucky, used = 0;
cin >> have >> m >> k >> lucky;
vector<int> a(k + 1);
for(int i = 1; i <= k; i ++)
cin >> a[i];
bool ok = true;
vi cnt(m + 1); // 统计个数
vector g(3, vi(3, -1)); // 记录图
vector<pii> pos = {{1,1}, {0,0}, {0,1}, {0,2}, {1,0}, {1,2}, {2,0}, {2,1}, {2,2}};
vector<array<int,3>> step3_line = { {0, 1, 8}, {0, 2, 7}, {0, 3, 6},
{0, 4, 5}, {1, 2, 3}, {1, 4, 6},
{3, 5, 8}, {6, 7, 8}};
auto solve = [&]() -> void {
bool opted = false, delSpecial = false;
auto val = [&](int i) -> int{
const auto &[x, y] = pos[i];
return g[x][y];
};
auto add = [&](int i) -> void {
const auto &[x, y] = pos[i];
opted = true;
have --;
used ++;
cnt[a[used]] ++;
g[x][y] = a[used];
if(a[used] == 0) {
have ++;
g[x][y] = -1;
} else if(a[used] == lucky) {
have ++;
}
return;
};
auto del = [&](int i, bool special = false) -> void {
if(i == 0 and special == false) {
delSpecial = true;
return;
}
const auto &[x, y] = pos[i];
opted = true;
g[x][y] = -1;
return;
};
// step 1
for(int i = 0; i < 9; i ++)
while(val(i) == -1 and have > 0 and used < k)
add(i);
// step 2
set<int> cntStep2;
for(auto [x, y] : pos)
cntStep2.insert(g[x][y]);
if(cntStep2.size() == 9 and cntStep2.count(-1) == 0){
del(0, true);
return;
}
// step 3
for(const auto &[x, y, z] : step3_line) {
if(val(x) == -1) continue;
if(val(x) != val(y) or val(x) != val(z)) continue;
del(x), del(y), del(z);
have += 5; // 奖励
}
// step 4
for(int i = 0; i < 9; i ++ ) {
if(val(i) == -1) continue;
for(int j = i + 1; j < 9 and val(i) != -1 ; j ++) {
if(val(i) != val(j)) continue;
del(i), del(j);
have ++;
}
}
// step 5
del(0, delSpecial);
// step 6
if(opted == false){
ok = false;
return;
}
set<int> cntStep6;
for(auto [x, y] : pos)
cntStep6.insert(g[x][y]);
if(cntStep6.size() == 1 and *cntStep6.begin() == -1)
have += 10;
return;
};
while(ok)
solve();
for(auto i : cnt)
cout << i << " ";
cout << "\n";
if(have > 0) cout << "Unhappy! " << have << "\n";
return 0;
}