先把自己做出来的题目发出来
A.小A的签到题
这道题,真的傻乎乎的直接把代码提交上去了。。。果不其然,没过。
嗯,怎么做呢?只需要把代码放在文件里,然后尝试几个数据就知道答案了。
#include
using namespace std;
int main() {
long long n;
scanf("%lld",&n);
if (n%2 == 0) printf("-1");
else printf("1");
return 0;
}
B.小A的回文串
这是一个循环的回文串。
#include
#include
#include
using namespace std;
const int MAXN = 5005;
char s[MAXN];
int main () {
scanf("%s", s);
int len = strlen(s), ans = 0;
for (int i = 0; i < len; i++) {
int cnt = -1;
for (int a = i, b = i; s[a] == s[b]; a = (a-1+len)%len, b = (b+1)%len) {
if (cnt > len - 2) break;
cnt = cnt+2;
}
ans = max(ans, cnt);
}
for (int i = 0; i < len; i++) {
int cnt = 0;
for (int a = (i-1+len)%len, b = i; s[a] == s[b]; a = (a-1+len)%len, b = (b+1)%len) {
if (cnt > len - 2) break;
cnt = cnt+2;
}
ans = max(ans, cnt);
}
printf("%d", ans);
return 0;
}
C. 小A买彩票
嗯,这是一道数学题。先说一个可以用到的公式:
因为n<=30可以先把所有的组合数写出来,然后在三重循环d张4元,c张3元,b张2元,把所有大于等于3*n的可能情况加起来,再除以所有可能的情况4^n。分子和分母的公约数只有可能是2的次方。
#include
#include
using namespace std;
const int MAXN = 35;
unsigned long long C[MAXN][MAXN];
int main () {
int n;
scanf("%d", &n);
for (int i = 0; i <= n; i++) {
C[i][0] = 1;
C[0][i] = 1;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= i; j++) {
C[j][i] = ((i-j+1) * C[j-1][i]) / j;
}
}
unsigned long long fenzi = 0;
for (int d = n; d >= 0; d--) {
for (int c = n-d; c >= 0; c--) {
for (int b = n-d-c; b >= 0; b--) {
int a = n-d-c-b;
if (d*4+c*3+b*2+a >= n*3) {
fenzi += C[d][n]*C[c][n-d]*C[b][n-d-c];
}
}
}
}
n = 2*n;
while (fenzi%2 == 0) {
fenzi = fenzi/2;
n--;
}
unsigned long long fenmu = pow(2, n);
printf("%llu/%llu", fenzi, fenmu);
return 0;
}
D. 小A的位运算
#include
#include
using namespace std;
const int MAXN = 5000010;
int N, p[MAXN], L[MAXN], R[MAXN];
int main () {
scanf("%d", &N);
int minV = 1e9;
for (int i = 0; i < N; i++) {
scanf("%d", &p[i]);
}
for (int i = 1; i < N; i++) {
L[i] = L[i-1] | p[i-1];
}
for (int i = N-2; i >= 0; i--) {
R[i] = R[i+1] | p[i+1];
}
int ans = 0;
for (int i = 0; i < N; i++) {
ans = max(ans, L[i] | R[i]);
}
printf("%d", ans);
return 0;
}
H.小A的柱状图
这道题,竟然暴力可解,果然是数据太水了。。。
#include
#include
using namespace std;
const int MAXN = 1e6+10;
unsigned long long A[MAXN], H[MAXN];
int n;
int main () {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%llu", &A[i]);
}
for (int i = 1; i <= n; i++) {
scanf("%llu", &H[i]);
}
unsigned long long ans = 0;
for (int i = 1; i <= n; i++) {
int w = A[i];
for (int j = i-1; H[j] >= H[i] && j >= 1; j--) {
w += A[j];
}
for (int j = i+1; H[j] >= H[i] && j <= n; j++) {
w += A[j];
}
ans = max(ans, w * H[i]);
}
printf("%llu", ans);
return 0;
}
嗯,但是我还是需要学习一下更加精妙的解法,也就是单调栈。
代码如下:
#include
#include
#include
#define LL long long
using namespace std;
const int MAXN = 1e6+10;
LL W[MAXN], H[MAXN], L[MAXN], R[MAXN];
//W[i]为从0到i的宽度,L[i]为向左扩展的最大宽度,直至遇到比H[i]小,R[i]为向右扩展直至遇到比H[i]小的
int n;
int main () {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%llu", &W[i]);
W[i] += W[i-1];
}
for (int i = 1; i <= n; i++) {
scanf("%llu", &H[i]);
}
stack sh, sw;
sh.push(-1); sw.push(0); // 防止溢出
for (int i = 1; i <= n; i++) {
while (sh.top() >= H[i]) {
sh.pop(); sw.pop();
}
L[i] = sw.top();
sh.push(H[i]); sw.push(W[i]);
}
sh.push(-1); sw.push(W[n]);
for (int i = n; i >= 1; i--) {
while (sh.top() >= H[i]) {
sh.pop(); sw.pop();
}
R[i] = sw.top();
sh.push(H[i]); sw.push(W[i-1]);
}
LL ans = 0;
for (int i = 1; i <= n; i++) {
ans = max(ans, (R[i] - L[i]) * H[i]);
}
printf("%llu", ans);
return 0;
}
以上就是我做出来的五道题,下面就是我需要补的题。
E.小A的路径
这道题离散数学学过,并且用到矩阵快速幂,可惜的是我没学快速幂,并且当时也根本没有想到离散数学。
先记录一下快速幂的模板:
int pow2(int a, int b) {
int ans = 1, base = a;
while (b != 0) {
if (b & 1 != 0) {
ans *= base;
}
base *= base;
b >>= 1;
}
return ans;
}
矩阵快速幂的模板:
#include
using namespace std;
#define LL long long
const LL MOD = 1000000007;
const int MAXN = 105;
struct Matrix {
LL m[MAXN][MAXN];
};
Matrix m;
LL k;
int n;
// 矩阵的乘法
Matrix mul (Matrix a, Matrix b) {
Matrix c;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
c.m[i][j] = 0;
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
for (int k = 0; k < n; k++) {
c.m[i][j] = c.m[i][j] % MOD + a.m[i][k]*b.m[k][j] % MOD;
}
}
}
return c;
}
// 矩阵的快速幂
Matrix pow2 (Matrix a) {
Matrix ans;
for (int i = 0; i < n; i++) {
ans.m[i][i] = 1;
}
while (k != 0) {
if ((k&1) != 0) {
ans = mul(ans, a);
}
a = mul(a, a);
k = k >> 1;
}
return ans;
}
int main () {
scanf("%d %llu", &n, &k);
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
scanf("%llu", &m.m[i][j]);
}
}
Matrix ans = pow2(m);
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (j != 0) printf(" ");
printf("%lld", (ans.m[i][j])%MOD);
}
printf("\n");
}
return 0;
}
小白月赛E题的AC代码如下:
#include
using namespace std;
#define ULL unsigned long long
const int MAXN = 105;
const int MOD = 1000000007;
struct Matrix {
ULL m[MAXN][MAXN];
};
int N, M, K, S;
Matrix mul (Matrix a, Matrix b) {
Matrix c;
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++) {
c.m[i][j] = 0;
for (int k = 1; k <= N; k++) {
c.m[i][j] = c.m[i][j] % MOD + a.m[i][k] * b.m[k][j] % MOD;
}
}
}
return c;
}
Matrix pow2 (Matrix a) {
Matrix ans;
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++) {
if (i == j) ans.m[i][i] = 1;
else ans.m[i][j] = 0;
}
}
while (K) {
if (K&1) {
ans = mul(ans, a);
}
a = mul(a, a);
K = K >> 1;
}
return ans;
}
int main () {
scanf("%d %d %d %d", &N, &M, &K, &S);
Matrix a;
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++) {
a.m[i][j] = 0;
}
}
for (int i = 0, u, v; i < M; i++) {
scanf("%d%d", &u, &v);
a.m[u][v]++;
}
Matrix m = pow2(a);
ULL ans = 0;
for (int i = 1; i <= N; i++) {
if (i != S) ans = (ans + m.m[S][i]) % MOD;
}
printf("%llu", ans % MOD);
return 0;
}
F. 小A的最短路
这是一颗树,然后再加上了一条长度为0的边。可以把他分成两种情况,一种是走长度为0的边,一种是不走长度为0的边。
如果不走长度为0的边,那么x和y之间的距离就是: depth[x] + depth[y] - 2 * depth[ lca[x][y]].
如果走长度为0的边,x与y之间的距离,就等dis(x->u) + dis(y->u)
我的AC代码:
#include
#include
#include
using namespace std;
const int MAXN = 300005;
int depth[MAXN], n, dis[MAXN], U, V, Q;
vector edge[MAXN], father[MAXN]; // edge存储边,father存储祖先元素
void build (int index, int f) {
depth[index] = depth[f] + 1;
father[index].push_back(index);
for (int j = 0; j < father[f].size(); j++) {
father[index].push_back(father[f][j]);
}
for (int i = 0; i < edge[index].size(); i++) {
int u = edge[index][i];
if (depth[u] == 0) build(u, index);
}
}
void bfs () {
queue q;
q.push(U); q.push(V);
dis[U] = 1; dis[V] = 1;
while (!q.empty()) {
int index = q.front(); q.pop();
for (int i = 0; i < edge[index].size(); i++) {
int u = edge[index][i];
if (dis[u] != 0) continue;
dis[u] = dis[index] + 1;
q.push(u);
}
}
}
int main () {
scanf("%d", &n);
for (int i = 1, a, b; i < n; i++) {
scanf("%d%d", &a, &b);
edge[a].push_back(b);
edge[b].push_back(a);
}
// 假设1为根元素,开始建边
build(1, 0);
// 计算u点和v点互连后到各个点的距离
scanf("%d %d", &U, &V);
bfs();
// 开始查询
scanf("%d", &Q);
for (int i = 0, x, y; i < Q; i++) {
scanf("%d %d", &x, &y);
int dis1 = dis[x] + dis[y] - 2, lca;
if (depth[x] > depth[y]) {
for (int j = 0; j < father[y].size(); j++) {
if (father[y][j] == father[x][j+depth[x]-depth[y]]) {
lca = father[y][j];
break;
}
}
} else {
for (int j = 0; j < father[x].size(); j++) {
if (father[x][j] == father[y][j+depth[y]-depth[x]]) {
lca = father[x][j];
break;
}
}
}
printf("%d\n", min(dis1, depth[x]+depth[y]-2*depth[lca]));
}
return 0;
}
G. 小A和小B
其实这道题在月赛时已经写的差不多了,通过了80%的测试点,然而我现在还是没有找到错误。
先记录一下错误代码,是先让A走完所有的可能再让B走,直到B走的步数大于等于A,此时B走的步数就是最终结果:
#include
#include
#include
using namespace std;
const int MAXN = 1005;
const int dr[]={0,0,1,-1,1,-1,1,-1};
const int dc[]={-1,1,0,0,1,-1,-1,1};
int N, M;
int d1[MAXN][MAXN], d2[MAXN][MAXN];
char mi[MAXN][MAXN];
bool vis[MAXN][MAXN];
bool inside (int i, int j) {
return i >= 0 && j >= 0 && i < N && j < M;
}
int main () {
int endR, endC;
scanf("%d %d", &N, &M);
queue qr, qc;
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
getchar();
scanf("%c", &mi[i][j]);
if (mi[i][j] == 'C') {
qr.push(i);
qc.push(j);
vis[i][j] = true;
} else if (mi[i][j] == 'D') {
endR = i;
endC = j;
}
}
}
while (!qr.empty()) {
int r = qr.front(), c = qc.front();
qr.pop(); qc.pop();
for (int i = 0; i < 8; i++) {
int nr = r + dr[i], nc = c + dc[i];
if (inside(nr, nc) && !vis[nr][nc] && mi[nr][nc] != '#') {
qr.push(nr); qc.push(nc);
vis[nr][nc] = true;
d1[nr][nc] = d1[r][c] + 1;
}
}
}
if (!vis[endR][endC]) {
printf("NO\n");
} else {
bool ok = false;
memset(vis, 0, sizeof(vis));
vis[endR][endC] = true;
qr.push(endR); qc.push(endC);
d2[endR][endC] = 1;
while (!qr.empty() && !ok) {
int r = qr.front(), c = qc.front();
qr.pop(); qc.pop();
for (int i = 0; i < 4; i++) {
int nr = r + dr[i], nc = c + dc[i];
if (inside(nr, nc) && !vis[nr][nc] && mi[nr][nc] != '#') {
qr.push(nr); qc.push(nc);
vis[nr][nc] = true;
d2[nr][nc] = d2[r][c] + 1;
if (d1[nr][nc] <= d2[nr][nc]/2) {
printf("YES\n%d", d2[nr][nc]/2);
ok = true;
break;
}
}
}
}
}
return 0;
}
以下这份是用双向bfs写的AC代码:
#include
#include
using namespace std;
const int MAXN = 1005;
const int dr[]={0,0,1,-1,1,-1,1,-1};
const int dc[]={-1,1,0,0,1,-1,-1,1};
int N, M;
bool vis[2][MAXN][MAXN];
char mi[MAXN][MAXN];
queue qr[2], qc[2];
bool inside (int i, int j) {
return i >= 0 && j >= 0 && i < N && j < M;
}
void read () {
scanf("%d %d", &N, &M);
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
getchar();
scanf("%c", &mi[i][j]);
if (mi[i][j] == 'C') {
qr[0].push(i);
qc[0].push(j);
vis[0][i][j] = true;
} else if (mi[i][j] == 'D') {
qr[1].push(i);
qc[1].push(j);
vis[1][i][j] = true;
}
}
}
}
bool bfs (int dir, int len) {
int size = qr[dir].size();
while (size--) {
int r = qr[dir].front(), c = qc[dir].front();
qr[dir].pop(); qc[dir].pop();
for (int i = 0; i < len; i++) {
int nr = r + dr[i], nc = c + dc[i];
if (inside(nr, nc) && !vis[dir][nr][nc] && mi[nr][nc] != '#') {
if (vis[1-dir][nr][nc]) return true;
qr[dir].push(nr); qc[dir].push(nc);
vis[dir][nr][nc] = true;
}
}
}
return false;
}
int main () {
read();
bool ok = false;
for (int i = 1; !qr[0].empty() || qr[1].empty(); i++) {
if (bfs(0, 8) || bfs(1, 4) || bfs(1, 4)) {
ok = true;
printf("YES\n%d", i);
break;
}
}
if (!ok) printf("NO\n");
return 0;
}
I. 小A取石子
我第一直觉感觉像是博弈论,然后就直接放弃了,因为根本不会。今天补了一下,在网上看到这叫做nim游戏,对于nim游戏先手(也就是小A) 赢的情况是当且仅当a1 ^ a2 ^ … ^ an != 0。
至于为什么是这样:
只要懂了这个的话,这道题就非常简单了,如果a1 ^ a2 ^ … ^ an != 0或者可以拿走石子的话A就赢了。
AC代码:
#include
using namespace std;
const int MAXN = 100010;
int N, K;
int main () {
scanf("%d%d", &N, &K);
int a = 0, b;
bool ok = false;
for (int i = 1; i <= N; i++) {
scanf("%d", &b);
a = a ^ b;
if (b >= K && K != 0) ok = true; // 可以拿走石头
}
if (a != 0 || ok) printf("YES");
else printf("NO");
return 0;
}
J. 小A的数学题
应该是数论里的内容,还不知道是什么知识,暂时先留在这里,等过一段时间再来补。
好了,以上就是所有的题目了。最后的最后,来个总结:在这次小白月赛中,我学了: