版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39972971/article/details/89035342
【比赛链接】
【题解链接】
【A】 The Beatles
【思路要点】
- 任取一个合法的起始点,枚举所有可能的步长 ,计算步数 ,取最优值即可。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e5 + 5; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } ll gcd(ll x, ll y) { if (y == 0) return x; else return gcd(y, x % y); } ll n, k, a, b, Max, Min; void work(ll x) { for (int i = 1; i <= n; i++) { chkmax(Max, gcd(x, n * k)); chkmin(Min, gcd(x, n * k)); x += k; } } int main() { read(n), read(k); read(a), read(b); if (a < b) swap(a, b); Max = 1, Min = n * k; work(a - b), work(k - a - b); write(n * k / Max), putchar(' '), writeln(n * k / Min); return 0; }
【B】 Lynyrd Skynyrd
【思路要点】
- 不妨令给定的排列为 。
- 对于一个序列中的位置 ,令 是最小的满足 的位置,连边 ,不难发现询问等价于询问区间内最长链的长度是否达到 。
- 倍增找到每一个位置出发走 步到达的位置,再利用前缀和 询问即可。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e5 + 5; const int MAXLOG = 20; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int n, m, q, a[MAXN], nxt[MAXN], last[MAXN]; int Max[MAXN], travel[MAXN][MAXLOG]; int main() { read(n), read(m), read(q); for (int i = 1; i <= n; i++) read(a[i]), nxt[a[i]] = a[i - 1]; nxt[a[1]] = a[n]; for (int i = 1; i <= m; i++) { int x; read(x); travel[i][0] = last[nxt[x]]; for (int j = 1; j < MAXLOG; j++) travel[i][j] = travel[travel[i][j - 1]][j - 1]; int pos = i; for (int j = 0; j < MAXLOG; j++) if ((1 << j) & (n - 1)) pos = travel[pos][j]; Max[i] = max(Max[i - 1], pos); last[x] = i; } for (int i = 1; i <= q; i++) { int x, y; read(x), read(y); if (Max[y] >= x) putchar('1'); else putchar('0'); } return 0; }
【C】 U2
【思路要点】
- 单独考虑每一个点 对抛物线 的限制,有 。
- 该限制显然是一个半平面,由于确定一条抛物线需要两个点,我们需要计算的是半平面交上顶点的个数。
- 由于本题值域较大,而半平面交需要计算直线交点,亲测直接采用半平面交难以通过。
- 考虑对上式进行变形,即变形为 。
- 注意到不等式左侧是一条直线,若将 看成一个点, 不等式的限制等价于直线 需要高过点 ,因此直接求点集 的上凸壳即可。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e5 + 5; typedef long long ll; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } struct point {ll x, y; } a[MAXN]; point operator + (point a, point b) {return (point) {a.x + b.x, a.y + b.y}; } point operator - (point a, point b) {return (point) {a.x - b.x, a.y - b.y}; } point operator * (point a, int b) {return (point) {a.x * b, a.y * b}; } long long operator * (point a, point b) {return 1ll * a.x * b.y - 1ll * a.y * b.x; } bool operator < (point a, point b) { if (a.x == b.x) return a.y < b.y; else return a.x > b.x; } int main() { int n; read(n); for (int i = 1; i <= n; i++) { ll x, y; read(x), read(y); a[i] = (point) {x, y - x * x}; } sort(a + 1, a + n + 1); int top = 1; for (int i = 2; i <= n; i++) { if (a[top].x == a[i].x) top--; while (top >= 2 && (a[top] - a[top - 1]) * (a[i] - a[top - 1]) <= 0) top--; a[++top] = a[i]; } printf("%d\n", top - 1); return 0; }
【D】 Foreigner
【思路要点】
- 我们需要为 数设计一个状态,使得我们可以方便地判断在某个 数后增加一个字符后得到的是不是 数,并进一步得到其状态。
- 本题较为关键的一点在于 ,因此若我们在状态中记录当前 数的排名 ,长度不足该数的 数的个数 ,相同长度的 数的个数 三数在模 意义下的结果,就可以达到上述目的。
- 此后,我们可以搜出所有可行的状态数 ,并建立一个大小为 的自动机。
- 在自动机上简单 即可得到各个位置出发的最长 数的长度。
- 时间复杂度 ,其中 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e5 + 5; const int MAXS = 105; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int tot, num[11][11][11]; int trans[MAXS][10]; void dfs(int rnk, int lst, int cur) { if (num[rnk][lst][cur]) return; num[rnk][lst][cur] = ++tot; int from = (lst + cur) % 11; for (int i = (lst + 1) % 11; i != rnk; i = (i + 1) % 11) from = (from + i) % 11; int nxt = 0; for (int i = lst; i != (lst + cur) % 11; i = (i + 1) % 11) nxt = (nxt + i + 1) % 11; for (int i = 1; i <= rnk; i++) { dfs((from + i) % 11, (lst + cur) % 11, nxt); trans[num[rnk][lst][cur]][i - 1] = num[(from + i) % 11][(lst + cur) % 11][nxt]; } } void initstates() { for (int i = 1; i <= 9; i++) dfs(i, 0, 9); //cerr << tot << endl; } char s[MAXN]; int n; int dp[MAXN][MAXS]; int main() { initstates(); scanf("%s", s + 1); n = strlen(s + 1); ll ans = 0; for (int i = n; i >= 1; i--) { if (i == n) { for (int j = 1; j <= tot; j++) dp[i][j] = i; } else { for (int j = 1; j <= tot; j++) { if (trans[j][s[i + 1] - '0']) dp[i][j] = dp[i + 1][trans[j][s[i + 1] - '0']]; else dp[i][j] = i; } } if (s[i] != '0') { int now = dp[i][num[s[i] - '0'][0][9]]; ans += now - i + 1; } } writeln(ans); return 0; }
【E】 Pink Floyd
【思路要点】
- 考虑 的做法,我们只需要每次询问一对没有删去的点,删去被指向的点即可。
- 时,我们找到的一对没有删去的点之间可能已经存在粉边,我们需要一些改进。
- 将给出的粉图求强联通分量,并缩点,我们会得到一系列入度为 的强联通分量,若只有一个入度为 的强联通分量,那么输出其中任意一点即可;否则,找到所属入度为 的强联通分量不同的两点,并套用 的做法,由于这两个强联通分量入度均为 ,因此这两点之间一定不存在粉边。
- 当一个强联通分量被删完后,将其删去,并断开其连出的边,检查是否有新的入度为 的强联通分量产生即可。
- 时间复杂度 ,询问次数不超过 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e5 + 5; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int n, m, timer, dfn[MAXN], low[MAXN]; int tot, belong[MAXN], top, Stack[MAXN]; vector <int> a[MAXN], b[MAXN], s[MAXN]; bool instack[MAXN]; int d[MAXN]; void tarjan(int pos) { dfn[pos] = low[pos] = ++timer; Stack[++top] = pos; instack[pos] = true; for (auto x : a[pos]) { if (!dfn[x]) { tarjan(x); chkmin(low[pos], low[x]); } else if (instack[x]) chkmin(low[pos], dfn[x]); } if (low[pos] == dfn[pos]) { int tmp = Stack[top--]; instack[tmp] = false; belong[tmp] = ++tot; while (tmp != pos) { tmp = Stack[top--]; instack[tmp] = false; belong[tmp] = tot; } } } int main() { read(n), read(m); for (int i = 1; i <= m; i++) { int x, y; read(x), read(y); a[x].push_back(y); } for (int i = 1; i <= n; i++) { if (!dfn[i]) tarjan(i); s[belong[i]].push_back(i); for (auto x : a[i]) if (belong[i] != belong[x]) { b[belong[i]].push_back(belong[x]); d[belong[x]]++; } } static int q[MAXN]; int l = 0, r = -1; for (int i = 1; i <= tot; i++) if (d[i] == 0) q[++r] = i; while (l < r) { int x = q[l++], y = q[l++]; cout << "? " << s[x].back() << ' ' << s[y].back() << endl; bool res; read(res); if (res) { q[--l] = x; s[y].pop_back(); if (s[y].empty()) { for (auto p : b[y]) if (--d[p] == 0) q[++r] = p; } else q[--l] = y; } else { q[--l] = y; s[x].pop_back(); if (s[x].empty()) { for (auto p : b[x]) if (--d[p] == 0) q[++r] = p; } else q[--l] = x; } } cout << "! " << s[q[l]].back() << endl; return 0; }