【比赛链接】
【题解链接】
【A】 The Beatles
【思路要点】
- 任取一个合法的起始点,枚举所有可能的步长 l l l ,计算步数 N × k g c d ( N k , l ) \frac{N\times k}{gcd(Nk,l)} gcd(Nk,l)N×k ,取最优值即可。
- 时间复杂度 O ( N L o g V ) O(NLogV) O(NLogV) 。
【代码】
#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
【思路要点】
- 不妨令给定的排列为 ( 1 , 2 , 3 , . . . , N ) (1,2,3,...,N) (1,2,3,...,N) 。
- 对于一个序列中的位置 a i a_i ai ,令 j j j 是最小的满足 j > i , a j = a i % N + 1 j>i,a_j=a_i\%N+1 j>i,aj=ai%N+1 的位置,连边 i → j i\rightarrow j i→j ,不难发现询问等价于询问区间内最长链的长度是否达到 N N N 。
- 倍增找到每一个位置出发走 N − 1 N-1 N−1 步到达的位置,再利用前缀和 O ( 1 ) O(1) O(1) 询问即可。
- 时间复杂度 O ( N + M L o g N + Q ) O(N+MLogN+Q) O(N+MLogN+Q) 。
【代码】
#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
【思路要点】
- 单独考虑每一个点 ( x , y ) (x,y) (x,y) 对抛物线 ( b , c ) (b,c) (b,c) 的限制,有 x 2 + b x + c ≥ y x^2+bx+c\geq y x2+bx+c≥y 。
- 该限制显然是一个半平面,由于确定一条抛物线需要两个点,我们需要计算的是半平面交上顶点的个数。
- 由于本题值域较大,而半平面交需要计算直线交点,亲测直接采用半平面交难以通过。
- 考虑对上式进行变形,即变形为 b x + c ≥ y − x 2 bx+c\geq y-x^2 bx+c≥y−x2 。
- 注意到不等式左侧是一条直线,若将 ( x , y − x 2 ) (x,y-x^2) (x,y−x2) 看成一个点, 不等式的限制等价于直线 b x + c bx+c bx+c 需要高过点 ( x , y − x 2 ) (x,y-x^2) (x,y−x2) ,因此直接求点集 ( x , y − x 2 ) (x,y-x^2) (x,y−x2) 的上凸壳即可。
- 时间复杂度 O ( N L o g N ) O(NLogN) O(NLogN) 。
【代码】
#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
【思路要点】
- 我们需要为 i n a d e q u a t e inadequate inadequate 数设计一个状态,使得我们可以方便地判断在某个 i n a d e q u a t e inadequate inadequate 数后增加一个字符后得到的是不是 i n a d e q u a t e inadequate inadequate 数,并进一步得到其状态。
- 本题较为关键的一点在于 1 + 2 + 3 + ⋯ + 10 = 55 ≡ 0 ( m o d 11 ) 1+2+3+\dots+10=55\equiv0\ (mod\ 11) 1+2+3+⋯+10=55≡0 (mod 11) ,因此若我们在状态中记录当前 i n a d e q u a t e inadequate inadequate 数的排名 r n k rnk rnk ,长度不足该数的 i n a d e q u a t e inadequate inadequate 数的个数 l s t lst lst ,相同长度的 i n a d e q u a t e inadequate inadequate 数的个数 c u r cur cur 三数在模 11 11 11 意义下的结果,就可以达到上述目的。
- 此后,我们可以搜出所有可行的状态数 S S S ,并建立一个大小为 S S S 的自动机。
- 在自动机上简单 d p dp dp 即可得到各个位置出发的最长 i n a d e q u a t e inadequate inadequate 数的长度。
- 时间复杂度 O ( N S ) O(NS) O(NS) ,其中 S = 31 S=31 S=31 。
【代码】
#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
【思路要点】
- 考虑 M = 0 M=0 M=0 的做法,我们只需要每次询问一对没有删去的点,删去被指向的点即可。
- M > 0 M>0 M>0 时,我们找到的一对没有删去的点之间可能已经存在粉边,我们需要一些改进。
- 将给出的粉图求强联通分量,并缩点,我们会得到一系列入度为 0 0 0 的强联通分量,若只有一个入度为 0 0 0 的强联通分量,那么输出其中任意一点即可;否则,找到所属入度为 0 0 0 的强联通分量不同的两点,并套用 M = 0 M=0 M=0 的做法,由于这两个强联通分量入度均为 0 0 0 ,因此这两点之间一定不存在粉边。
- 当一个强联通分量被删完后,将其删去,并断开其连出的边,检查是否有新的入度为 0 0 0 的强联通分量产生即可。
- 时间复杂度 O ( N + M ) O(N+M) O(N+M) ,询问次数不超过 N N N 。
【代码】
#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; }