A: CCA的数列
分析
签到题, 依照题意模拟即可. 只不过我写的等比数列有精度错误, 换成 的形式就过了.
时间复杂度:
代码实现
#include <cstdio>
const int N = 1e5;
int n;
long long A[N + 5];
int main() {
scanf ("%d", &n);
for (int i = 1; i <= n; ++i) scanf ("%lld", &A[i]);
long long a = A[2] - A[1];
long long c = A[2] % A[1];
bool F1, F2, F3;
F1 = F2 = F3 = true;
for (int i = 3; i <= n; ++i) {
if (A[i] - A[i - 1] != a) F1 = false;
if (A[i] * A[1] != A[i - 1] * A[2]) F2 = false;
if (A[i] % A[i - 1] != c) F3 = false;
} if (F1 || F2 || F3) printf ("YES\n");
else printf ("NO\n");
return 0;
} B: CCA的字符串
分析
闲话: 第一眼以为是
板子, 看了看数据范围可以用
. 紧接着发现模式串长度仅为
......果然我还是太
了.
可以自然地想到这样一种做法:
- 找到每一个字符串中的 "NowCoder"
- 尝试将左右端点向两边扩展.
但是这样做有点麻烦, 因为会算重.
原因在于位置不同的两个 "NowCoder" 有可能扩展出相同的左右端点.
容斥? 不需要. 对于每一个合法的串, 只在其包含的最后一个 "NowCoder" 处计算它的贡献.
也就是说, 计算包含第 个 "NowCoder" 的串时, 右端点最远只能到第
个 "NowCoder" 的 "e" 处. (即不能包含它)
时间复杂度:
代码实现
#include <cstdio>
#include <cstring>
const int N = 1e5;
int Len;
char Str[N + 5];
char T[N + 5];
int S[N + 5], nL;
int main() {
scanf ("%s", Str + 1);
Len = strlen (Str + 1);
T[1] = 'N', T[2] = 'o', T[3] = 'w', T[4] = 'C', T[5] = 'o', T[6] = 'd', T[7] = 'e', T[8] = 'r';
for (int i = 1; i <= Len - 7; ++i) {
bool Flag = true;
for (int j = 1; j <= 8; ++j)
if (Str[i + j - 1] != T[j]) Flag = false;
if (Flag) S[++nL] = i;
} long long Ans = 0;
for (int i = 1; i <= Len; ++i) {
int R = (i == nL)? Len : (S[i + 1] + 6);
Ans += 1LL * S[i] * (R - (S[i] + 8 - 1) + 1);
} printf ("%lld\n", Ans);
return 0;
} C: CCA的矩阵
分析
如果锤子是方方正正的话, 这题就是一个裸的二维前缀和. 现在是斜着锤, 但还是可以往这个方面想.
画个图想想如何二维前缀和, 发现实际上只要维护一些倒三角形就行了. 其实本质上还是个二维前缀和, 只不过形状变了下而已.
时间复杂度:
代码实现
#include <cstdio>
#include <algorithm>
typedef long long LL;
const int N = 2e3;
int n, K;
int Map[N + 5][N + 5];
LL S[N + 5][N + 5];
int main() {
scanf ("%d%d", &n, &K);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j) scanf ("%d", &Map[i][j]);
for (int i = 1; i <= n; ++i) {
S[i][0] = S[i - 1][1];
S[i][n + 1] = S[i - 1][n];
for (int j = 1; j <= n; ++j) {
S[i][j] = Map[i][j] + Map[i - 1][j] + S[i - 1][j - 1] + S[i - 1][j + 1];
if (i - 2 >= 0) S[i][j] -= S[i - 2][j];
}
} LL Ans = 0;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j) {
int X1 = i - (2 * K - 1) + 1, Y1 = j;
int X2 = i - K + 1, Y2 = j - K + 1;
int X3 = i - K + 1, Y3 = j + K - 1;
if (X1 < 1 || X2 < 1 || Y2 < 1 || X3 < 1 || Y3 > n) continue;
LL tap = S[i][j] - S[X2][Y2] - S[X3][Y3] + S[X1][Y1] + Map[X1][Y1];
for (int p = 2; p <= K; ++p) tap += Map[X1 + p - 1][Y1 - (p - 1)] + Map[X1 + p - 1][Y1 + (p - 1)];
Ans = std::max (Ans, tap);
}
printf ("%lld\n", Ans);
return 0;
} D: CCA的图
分析
本场考试最好想的题. 没有之一.
读完题目, 实际上有两个子问题:
- 找出
- 找出
解决问题 的时候实际上有条件
. 把
从大到小排序, 跑一遍
, 到
与
首次连通时退出. 最后一条边的
即为
.
考虑问题 , 首先把
的边弄出来, 把
从小到大排序. 跑一遍
, 到
与
首次连通时退出. 最后一条边的
即为
.
完了? 完了.
代码实现
#include <cstdio>
#include <algorithm>
const int N = 1e6;
const int M = 3e6;
int n, m, S, T;
int mL;
struct Edge { int u, v, w; } E[M + 5], Ed[M + 5];
int Fa[N + 5];
int AnsL, AnsR;
bool Cmp1 (Edge, Edge);
bool Cmp2 (Edge, Edge);
int Kruskal1();
int Kruskal2();
int Find (int);
int main() {
scanf ("%d%d%d%d", &n, &m, &S, &T);
for (int i = 1; i <= m; ++i) scanf ("%d%d%d", &E[i].u, &E[i].v, &E[i].w);
std::sort (E + 1, E + 1 + m, Cmp1);
AnsL = Kruskal1();
std::sort (Ed + 1, Ed + 1 + mL, Cmp2);
AnsR = Kruskal2();
printf ("%d %d\n", AnsL, AnsR);
return 0;
} bool Cmp1 (Edge X, Edge Y) {
return X.w > Y.w;
} bool Cmp2 (Edge X, Edge Y) {
return X.w < Y.w;
} int Find (int p) {
if (p == Fa[p]) return p;
else return Fa[p] = Find (Fa[p]);
} int Kruskal1() {
for (int i = 1; i <= n; ++i) Fa[i] = i;
for (int i = 1; i <= m; ++i) {
Fa[Find (E[i].u)] = Find (E[i].v);
Ed[++mL] = E[i];
if (Find (S) == Find (T)) return E[i].w;
} return 0;
} int Kruskal2() {
for (int i = 1; i <= n; ++i) Fa[i] = i;
for (int i = 1; i <= mL; ++i) {
Fa[Find (Ed[i].u)] = Find (Ed[i].v);
if (Find (S) == Find (T)) return Ed[i].w;
} return 0;
} 
京公网安备 11010502036488号