2021CCPC女生赛
这篇题解题目的顺序是按照我认为的难度顺序来的。
K.音乐游戏
把每一行的字符串读进来之后,直接去计算这个字符串中有多少个\("-"\)字符就可以了
int n; std::cin >> n;
i64 ans = 0;
rep(i,0,n + 1) { // for (int i = 0; i < n + 1; i ++ ) 读到n + 1的原因是getline读取字符串的时候好像会多读取一个空串进来
std::string s;
std::getline(std::cin,s);
ans += count(all(s), '-');
}
std::cout << ans << "\n";
G.3G网络
直接看样例的输入和输出可以猜出来,直接输出\(\frac{1}{n}\)就可以了。
int n; std::cin >> n;
std::cout << std::fixed << std::setprecision(20) << 1.0 / n << "\n";
在输出的时候注意保留小数的位数,cout << 1.0 / n;会wa一发。
D.修建道路
可能有些人觉得是最小生成树,但是这个题其实就是一个很简单的贪心,我们直接把原序列上相邻的两个点连边就可以了。
int n; std::cin >> n;
std::vector<int> a(n + 1);
for (int i = 1; i <= n; i ++ ) std::cin >> a[i];
i64 ans = 0;
rep(i,1,n) ans += std::max(a[i], a[i + 1]);
std::cout << ans << "\n";
A.公交线路
我们从因为我们一开始并不能确定是往那边走,所以我们从起点开始,向前向后分别走\(m\)的长度,分别判断在这两种情况中是否坐错了,如果前后扫的时候都没有发现做错,那就输出Unsure。
int n, x, y;
std::cin >> n >> x >> y;
std::vector<int> a(n + 1);
rep(i,1,n + 1) std::cin >> a[i];
int m; std::cin >> m;
std::vector<int> b(m + 1);
rep(i,1,m + 1) std::cin >> b[i];
bool f = true, ff = true;
for (int i = x - 1; i >= x - m; i -- ) {
if (a[i] != b[x - i]) f = false;
if (i == 0) break;
}
for (int i = x + 1; i <= x + m; i ++ ) {
if (a[i] != b[i - x]) ff = false;
}
if (f && ff) std::cout << "Unsure\n";
else {
if (f) std::cout << (x > y ? "Right\n" : "Wrong\n");
else if (ff) std::cout << (x > y ? "Wrong\n" : "Right\n");
}
I.驾驶卡丁车
纯模拟题,注意读题判断细节就好了,一开始小丁车朝向是向上的,而且每一次操作的时候都要旋转\(45^{\circ}\),所以为了操作方便,我们按照顺时针的方式建立方向数组。
int dx[8] = {-1, -1, 0, 1, 1, 1, 0, -1};
int dy[8] = {0, 1, 1, 1, 0, -1, -1, -1};
每一次向前走\(v\)的长度的距离,在这个路上可能会遇上障碍,所以我们不能直接让小卡车直接过去,要\(for\)循环一点一点的走,每一步都要去判断,如果是斜着走的话两边不可以都是障碍。
int n, m;
std::cin >> n >> m;
std::vector<std::vector<char>> G(n + 1, std::vector<char> (m + 1));
int x = 0, y = 0;
for (int i = 1; i <= n; i ++ ) {
for (int j = 1; j <= m; j ++ ) {
std::cin >> G[i][j];
if (G[i][j] == '*') x = i, y = j;
}
}
int q; std::cin >> q;
int v = 0;
int cur = 0;
std::string op; std::cin >> op;
rep(i,0,q) {
if (op[i] == 'U') v ++;
if (op[i] == 'D') v = std::max(v - 1, 0);
if (op[i] == 'L') cur = (cur - 1 + 8) % 8;
if (op[i] == 'R') cur = (cur + 1) % 8;
int nx, ny;
bool flag = true;
rep(xx, 0, v) {
nx = x + dx[cur], ny = dy[cur] + y;
if (nx < 1 || nx > n || ny < 1 || ny > m || G[nx][ny] == '#') {
std::cout << "Crash! " << x << " " << y << "\n";
v = 0;
flag = false;
break;
}
if (cur == 1 || cur == 3 || cur == 5 || cur == 7) {
if (G[x + dx[(cur - 1 + 8) % 8]][y + dy[(cur - 1 + 8) % 8]] == '#' &&
G[x + dx[(cur + 1) % 8]][y + dy[(cur + 1) % 8]] == '#') {
std::cout << "Crash! " << x << " " << y << "\n";
v = 0;
flag = false;
break;
}
}
x = nx, y = ny;
}
if (flag) std::cout << x << " " << y << "\n";
}
C.连锁商店
有\(n\)个景点,\(m\)条缆线,每一个景点都会有一个连锁店,那些连锁店\(i\)都会有它们隶属的公司\(c_i\),并且连锁店会给游客红包\(w_i\)元,不过每一个公司的红包只能拿一次.要求出从起点\(1\)开始都每一个景点能拿到的最大优惠红包是多少.
到达每一个景点的方式是不固定的,也就是我们要枚举出所有的状态来找出从起点开始到达节点的最有路径是多少。枚举的方式就是采用状压\(dp\)的方式来用\(01\)表示能否到达。由于\(n\)的范围是\(1\leq n \leq 36\),这个范围非常大,开数组的话根本开不出\(2^{36}\)的空间,所以这里考虑用\(dp[i]\)来表示二进制下的状态,\(i\)就是当前的景点。那么从\(i\)走到\(k\)节点可能有很多条路,比如说\(i \rightarrow i + 1 \rightarrow j \rightarrow k\) 或者是\(i \rightarrow k\)但是我们并不知道那一条路能拿到最多的红包,所以我们考虑用\(Floyd\)算法来找到最优的路径,路经的景点越多拿到的红包才可能多,所以我们在\(Floyd\)的时候应该将直接到达的边删掉,这样不会影响我们后面枚举所有状态的过程。
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= n; j ++ )
for (int k = 1; k <= j; k ++ )
if ((path[i] & (1ll << j)) && (path[i] & (1ll << k)) && (path[k] & (1ll << j)))
path[i] -= (1ll << j);
在状态转移的过程中,就有两种情况,一种是这个景点的公司的红包已经领过了那么状态转移方程就是\(dp[u] = dp[father]\), 另一种就是这个公司的红包没有拿过,那么状态转移方程就是\(dp[u] = dp[father] + w[c[u]]\),所以我们还需要一个数组来给景点的公司来打标记来确定最后的状态是什么
int n, m;
std::cin >> n >> m;
std::vector<int> c(n + 1), w(n + 1);
std::vector<i64> path(n + 1);
std::vector<i64> dp(n + 1);
rep(i,1,n + 1) std::cin>>c[i];
rep(i,1,n + 1) std::cin>>w[i];
rep(i,0,m) {
i64 u, v;
std::cin >> u >> v;
path[u] |= (1ll << v);
}
std::vector<int> vis(n + 1);
std::vector<i64> ans(n + 1);
std::function<void(int, int)> dfs = [&] (int u, int fa) -> void {
if (!vis[c[u]]) dp[u] = dp[fa] + w[c[u]];
else dp[u] = dp[fa];
ans[u] = std::max(ans[u], dp[u]);
vis[c[u]] ++;
rep(i,1,n + 1) {
if (path[u] & (1ll << i)) {
dfs(i, u);
vis[c[i]] --;
}
}
};
rep(i,1,n + 1) rep(j,1,n + 1) rep(k,1,j + 1)
if ((path[i] & (1ll << j)) && (path[i] & (1ll << k)) && (path[k] & (1ll << j)))
path[i] -= (1ll << j);
dfs(1, 0);
rep(i,1,n + 1) std::cout << ans[i] << "\n";
F.地图压缩
哈希一遍,然后将二维转化为一维之后,就是\(KMP\)直接找到行与列的最小循环节,最后的答案就是行与列最小循环节的长度的乘积
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 2009;
const ll p = 1331;
const ll mod = 998244353;
int n, q;
char mp[maxn][maxn];
ll col[maxn][maxn], row[maxn][maxn];
ll rcol[maxn][maxn], rrow[maxn][maxn];
ll mhuash[maxn];
ll rhuash[maxn];
ll s[maxn];
ll sr[maxn];
int nex[maxn];
int work(int len) {
for(int i = 2, j = 0; i <= len; ++ i){
while (j && s[i] != s[j + 1]){
j = nex[j];
}
if(s[i] == s[j + 1]){
++ j;
}
nex[i] = j;
}
return len - nex[len];
}
void pre(){
mhuash[0] = 1;
for(int i = 1; i <= n; ++ i)
mhuash[i] = mhuash[i - 1] * p % mod;
for(int i = 1; i <= n; ++ i){
for(int j = 1; j <= n; ++ j){
row[i][j] = (row[i][j - 1] * p + (mp[i][j] - 'a')) % mod;
col[j][i] = (col[j][i - 1] * p + (mp[i][j] - 'a')) % mod;
}
}
}
int main(){
std::cin.tie(nullptr)->sync_with_stdio(false);
cin >> n >> q;
for(int i = 1; i <= n; ++ i){
for(int j = 1; j <= n; ++ j){
cin >> mp[i][j];
}
}
pre();
while(q--){
int x1, x2, y1, y2;
cin >> x1 >> y1 >> x2 >> y2;
for(int i = x1; i <= x2; ++ i)
s[i - x1 + 1] = (row[i][y2] - row[i][y1 - 1] * mhuash[y2 - y1 + 1] % mod + mod) % mod;
int len = x2 - x1 + 1;
int ansx = work(len);
for(int i = y1; i <= y2; ++ i)
s[i - y1 + 1] = (col[i][x2] - col[i][x1 - 1] * mhuash[x2 - x1 + 1] % mod + mod) % mod;
len = y2 - y1 + 1;
int ansy = work(len);
cout << ansx * ansy << "\n";
}
}

浙公网安备 33010602011771号