2018年全国多校算法寒假训练营练习比赛(第五场)题解
【题目链接】
A - 逆序数
经典问题,有很多方法,例如树状数组,线段树,归并排序、分治等。代码不贴了。
单点修改求区间和,树状数组或者线段树都可以。
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5 + 10; long long c[maxn]; int lowbit(int x) { return x & (-x); } long long sum(int p) { long long res = 0; while(p) { res += c[p]; p -= lowbit(p); } return res; } void update(int x, long long y) { while(x < maxn) { c[x] += y; x += lowbit(x); } } int main() { int n, m; scanf("%d%d", &n, &m); for(int i = 1; i <= n; i ++) { long long x; scanf("%lld", &x); update(i, x); } while(m --) { int op; scanf("%d", &op); if(op == 1) { int x; long long y; scanf("%d%lld", &x, &y); update(x, y); } else { int L, R; scanf("%d%d", &L, &R); printf("%lld\n", sum(R) - sum(L - 1)); } } return 0; }
C - 字符串的问题
比赛中暴力从大到小枚举前缀长度,KMP验证一下就 AC 了?还要思考一下复杂度究竟对不对。
#include <iostream> #include <cstring> using namespace std; const int N = 1000010; int nx[N]; char S[N], T[N]; int slen, tlen; void getNext() { int j, k; j = 0; k = -1; nx[0] = -1; while(j < tlen) if(k == -1 || T[j] == T[k]) nx[++j] = ++k; else k = nx[k]; } /* 返回模式串在主串S中出现的次数 */ int KMP_Count() { int ans = 0; int i, j = 0; if(slen == 1 && tlen == 1) { if(S[0] == T[0]) return 1; else return 0; } getNext(); for(i = 0; i < slen; i++) { while(j > 0 && S[i] != T[j]) j = nx[j]; if(S[i] == T[j]) j++; if(j == tlen) { ans++; j = nx[j]; } } return ans; } /* int main() { int TT; int i, cc; cin>>TT; while(TT--) { cin>>S>>T; slen = strlen(S); tlen = strlen(T); cout<<"模式串T在主串S中首次出现的位置是: "<<KMP_Index()<<endl; cout<<"模式串T在主串S中出现的次数为: "<<KMP_Count()<<endl; } return 0; } */ int check(int x) { int p1 = 0; int p2 = slen - x; while(p1 <= x - 1) { if(S[p1] != S[p2]) return 0; p1 ++; p2 ++; } for(int i = 0; i < x; i ++) { T[i] = S[i]; T[i + 1] = 0; } tlen = x; if(KMP_Count() >= 3) return 1; return 0; } int main() { scanf("%s", S); slen = strlen(S); int ans = 0; int L = 1, R = slen; for(int i = slen - 1; i >= 1; i --) { if(check(i)) { ans = i; break; } } if(ans == 0) printf("Just a legend\n"); else { for(int i = 0; i < ans; i ++) { printf("%c", S[i]); } printf("\n"); } }
D - 集合问题
写了一堆暴力模拟规则就搞过去了,看到有并查集AC的,有待学习。
#include <bits/stdc++.h> using namespace std; const int maxn = 110000; int n; int A, B; int a[maxn]; map<int, int> belong; map<int, bool> flag; int main() { int fail = 0; scanf("%d%d%d", &n, &A, &B); for(int i = 1; i <= n; i ++) { scanf("%d", &a[i]); flag[a[i]] = 1; } sort(a + 1, a + 1 + n); if(A == B) { for(int i = 1; i <= n; i ++) { if(flag[B - a[i]] == 0) { fail = 1; break; } belong[a[i]] = 2; } } else { while(1) { if(fail) break; int ss = 0; for(int i = 1; i <= n; i ++) { if(belong[a[i]]) continue; if(flag[A - a[i]] == 0 && flag[B - a[i]] == 0) { fail = 1; break; } else if(flag[A - a[i]] == 1 && (flag[B - a[i]] == 0 || belong[B - a[i]])) { if(belong[A - a[i]] == 2) { fail = 1; break; } belong[a[i]] = 1; belong[A - a[i]] = 1; ss = 1; } else if((flag[A - a[i]] == 0 || belong[A - a[i]]) && flag[B - a[i]] == 1) { if(belong[B - a[i]] == 1) { fail = 1; break; } belong[a[i]] = 2; belong[B - a[i]] = 2; ss = 1; } else { if(A - a[i] != a[i] && B - a[i] != a[i]) { if(belong[A - a[i]] == 0 && belong[B - a[i]] == 0) { continue; } else if(belong[A - a[i]]) { belong[a[i]] = belong[A - a[i]]; ss = 1; } else { belong[a[i]] = belong[B - a[i]]; ss = 1; } } else if(A - a[i] == a[i]) { if(belong[B - a[i]] == 1) { fail = 1; break; } belong[a[i]] = 2; belong[B - a[i]] = 2; ss = 1; } else { if(belong[A - a[i]] == 2) { fail = 1; break; } belong[a[i]] = 1; belong[A - a[i]] = 1; ss = 1; } } } if(fail) break; if(ss == 0) break; } } if(fail == 0) { for(int i = 1; i <= n; i ++) { if(belong[a[i]] == 1 && belong[A - a[i]] != 1) fail = 1; if(belong[a[i]] == 2 && belong[B - a[i]] != 2) fail = 1; } } if(fail) { printf("NO\n"); } else { printf("YES\n"); for(int i = 1; i <= n; i ++) { printf("%d", belong[a[i]] - 1); if(i < n) printf(" "); else printf("\n"); } } return 0; }
E - 情人节的电灯泡
单点更新,求子矩阵和,二维树状数组即可。
#include <bits/stdc++.h> using namespace std; const int maxn = 1010; int n, m; int C[maxn][maxn]; int a[maxn][maxn]; int lowbit(int x) { return x & (-x); } void add(int x, int y, int c){ for(int i = x; i <= n; i += lowbit(i)){ for(int j = y; j <= n; j += lowbit(j)){ C[i][j] += c; } } } int sum(int x,int y){ int ret = 0; for(int i = x; i > 0; i -= lowbit(i)){ for(int j = y;j > 0;j -= lowbit(j)){ ret += C[i][j]; } } return ret; } int main() { scanf("%d%d", &n, &m); for(int i = 1; i <= n; i ++) { for(int j = 1; j <= n; j ++) { scanf("%d", &a[i][j]); add(i, j, a[i][j]); } } while(m --) { int op; scanf("%d", &op); if(op == 1) { int x, y; scanf("%d%d", &x, &y); if(a[x][y] == 1) { add(x, y, -1); } else { add(x, y, 1); } a[x][y] = (a[x][y] ^ 1); } else { int x1, y1, x2, y2; scanf("%d%d%d%d", &x1, &y1, &x2, &y2); printf("%d\n", sum(x2, y2) - sum(x1 - 1, y2) - sum(x2, y1 - 1) + sum(x1-1, y1-1)); } } return 0; }
模拟一下就可以了。
#include <bits/stdc++.h> using namespace std; long long n; long long work(long long x) { long long res = 0; while(x) { res += x % 10; x = x / 10; } return res; } int main() { scanf("%d", &n); while(1) { if(n <= 9) { printf("%lld\n", n); break; } n = work(n); } return 0; }
G - 送分啦-QAQ
该题和HDU 2516一样
H - Tree Recovery
区间加,区间求和,线段树就可以了。
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5 + 10; int n, Q; long long sum[4 * maxn]; long long f[4 * maxn]; void pushDown(int l, int r, int rt) { if(f[rt] == 0) return; int mid = (l + r) / 2; int len_left = (mid - l + 1); int len_right = (r - mid); f[2 * rt] += f[rt]; f[2 * rt + 1] += f[rt]; sum[2 * rt] += f[rt] * len_left; sum[2 * rt + 1] += f[rt] * len_right; f[rt] = 0; } void pushUp(int rt) { sum[rt] =sum[2 * rt] + sum[2 * rt + 1]; } void build(int l, int r, int rt) { if(l == r) { scanf("%lld", &sum[rt]); return; } int mid = (l + r) / 2; build(l, mid, 2 * rt); build(mid + 1, r, 2 * rt + 1); pushUp(rt); } void update(int L, int R, long long val, int l, int r, int rt) { if(L <= l && r <= R) { sum[rt] += val * (r - l + 1); f[rt] += val; return; } pushDown(l, r, rt); int mid = (l + r) / 2; if(L <= mid) update(L, R, val, l, mid, 2 * rt); if(R > mid) update(L, R, val, mid + 1, r, 2 * rt + 1); pushUp(rt); } long long query(int L, int R, int l, int r, int rt) { if(L <= l && r <= R) { return sum[rt]; } pushDown(l, r, rt); int mid = (l + r) / 2; long long left = 0; long long right = 0; if(L <= mid) left = query(L, R, l, mid, 2 * rt); if(R > mid) right = query(L, R, mid + 1, r, 2 * rt + 1); pushUp(rt); return left + right; } int main() { scanf("%d%d", &n, &Q); build(1, n, 1); while(Q --) { char op[10]; scanf("%s", op); if(op[0] == 'Q') { int L, R; scanf("%d%d", &L, &R); printf("%lld\n", query(L, R, 1, n, 1)); } else { int L; int R; long long val; scanf("%d%d%lld", &L, &R, &val); update(L, R, val, 1, n, 1); } } return 0; }