【比赛链接】
【题解链接】
**【A】**Find Square
【思路要点】
- 答案即为所有黑色方格坐标的平均值。
- 时间复杂度 O ( N ∗ M ) O(N*M) O(N∗M)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 100005; 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, sx, sy, cnt; int main() { read(n), read(m); for (int i = 1; i <= n; i++) { static char s[MAXN]; scanf("\n%s", s + 1); for (int j = 1; j <= m; j++) if (s[j] == 'B') { cnt++; sx += i; sy += j; } } printf("%d %d\n", sx / cnt, sy / cnt); return 0; }
**【B】**Unnatural Conditions
【思路要点】
- 我们可以构造两个和为 1 0 1000 10^{1000} 101000的数,使得它们能够回答任何输入。
- 令 A = 44444...445 , B = 55555...555 A=44444...445,B=55555...555 A=44444...445,B=55555...555即可。
- 时间复杂度 O ( N ) O(N) O(N)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 100005; 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 main() { string a, b; for (int i = 1; i <= 1000; i++) { a += '4'; b += '5'; } a += '5', b += '5'; cout << a << endl; cout << b << endl; return 0; }
**【C】**Rectangles
【思路要点】
- 用前/后缀和计算矩形的交,就可以在 O ( N ) O(N) O(N)的时间内得出除去任何一个矩形后其余矩形的交。
- 枚举去掉的矩形,若剩余矩形的交非空,输出其中任意一点。
- 时间复杂度 O ( N ) O(N) O(N)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 200005; const int INF = 1e9; 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 {int x, y; }; point min(point a, point b) {return (point) {min(a.x, b.x), min(a.y, b.y)}; } point max(point a, point b) {return (point) {max(a.x, b.x), max(a.y, b.y)}; } point l[MAXN], r[MAXN], prel[MAXN], prer[MAXN], sufl[MAXN], sufr[MAXN]; int n; int main() { read(n); for (int i = 1; i <= n; i++) { read(l[i].x), read(l[i].y); read(r[i].x), read(r[i].y); } prel[0] = sufl[n + 1] = (point) {-INF, -INF}; prer[0] = sufr[n + 1] = (point) {INF, INF}; for (int i = 1; i <= n; i++) { prel[i] = max(prel[i - 1], l[i]); prer[i] = min(prer[i - 1], r[i]); } for (int i = n; i >= 1; i--) { sufl[i] = max(sufl[i + 1], l[i]); sufr[i] = min(sufr[i + 1], r[i]); } for (int i = 1; i <= n; i++) { point tl = max(prel[i - 1], sufl[i + 1]); point tr = min(prer[i - 1], sufr[i + 1]); if (tl.x <= tr.x && tl.y <= tr.y) { printf("%d %d\n", tl.x, tl.y); return 0; } } return 0; }
**【D】**Order book
【思路要点】
- 考虑一个 A c c e p t Accept Accept操作带来的影响,被 A c c e p t Accept Accept的 o f f e r offer offer(令其价格为 x x x)的类型不能由此次操作确定,但当时存在的价格大于 x x x的 o f f e r offer offer必须为 s e l l sell sell,当时存在的价格小于 x x x的 o f f e r offer offer必须为 b u y buy buy。
- 在最后一个 A c c e p t Accept Accept操作前的 A d d Add Add操作加进的 o f f e r offer offer在上述过程考虑结束后若必须为 s e l l / b u y sell/buy sell/buy,则方案数不变;若同时必须是 s e l l sell sell和 b u y buy buy,则不存在合法方案;否则,它既可以是 s e l l sell sell,也可以是 b u y buy buy,方案数应当乘以 2 2 2(注意此时这个 o f f e r offer offer一定被 A c c e p t Accept Accept了)。
- 在最后一个 A c c e p t Accept Accept操作后的 A d d Add Add操作需要单独考虑,若一个 o f f e r offer offer被加入时已经小于某一个 b u y o f f e r buyoffer buyoffer,或大于某一个 s e l l o f f e r selloffer selloffer,那么它的类型时确定的,否则,它的类型是不确定的。记不确定的 o f f e r offer offer个数为 c n t cnt cnt,答案应当再乘以 c n t + 1 cnt+1 cnt+1。
- 用 s t d : : p r i o r i t y _ q u e u e std::priority\_queue std::priority_queue来模拟上述过程,时间复杂度 O ( N L o g N ) O(NLogN) O(NLogN)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 400005; const int P = 1e9 + 7; 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(""); } bool type[MAXN]; int x[MAXN]; map <int, bool> black, white, exist; priority_queue <int> hb, hw; int main() { int n; read(n); int last = 0; for (int i = 1; i <= n; i++) { char opt[15]; scanf("\n%s%d", opt + 1, &x[i]); type[i] = opt[2] == 'D'; if (!type[i]) last = i, exist[x[i]] = true; } for (int i = 1; i <= n; i++) if (type[i]) { hb.push(-x[i]); hw.push(x[i]); } else { while (!hb.empty() && hb.top() >= -x[i]) { if (hb.top() != -x[i]) black[-hb.top()] = true; hb.pop(); } while (!hw.empty() && hw.top() >= x[i]) { if (hw.top() != x[i]) white[hw.top()] = true; hw.pop(); } } int ans = 1, Max = 0, Min = 1e9; for (int i = 1; i <= last; i++) if (type[i]) { int tmp = 0; tmp += !black[x[i]]; if (black[x[i]] && !exist[x[i]]) chkmax(Max, x[i]); tmp += !white[x[i]]; if (white[x[i]] && !exist[x[i]]) chkmin(Min, x[i]); ans = ans * tmp % P; } int cnt = 1; for (int i = last + 1; i <= n; i++) if (x[i] >= Max && x[i] <= Min) cnt++; writeln(1ll * ans * cnt % P); return 0; }
**【E】**Restore Array
【思路要点】
- 我们首先考虑所有数都相等的情况。
- 若所有数都等于 0 0 0,那么任意一组所有数都相同的解都可以作为答案。
- 若所有数都相等,且不为 0 0 0,那么问题无解。
- 接下来我们认为至少存在两个数不等。
- 注意到 ( a + b ) % b = a ( b > a ) (a+b)\%b=a(b>a) (a+b)%b=a(b>a)。
- 令 M a x = m a x { b i } Max=max\{b_i\} Max=max{bi},取 b i b_i bi使得 b i = M a x , b i − 1 ≠ M a x b_i=Max,b_{i-1}\ne Max bi=Max,bi−1̸=Max,令 a i = b i a_i=b_i ai=bi。
- 若 b i − 1 = 0 b_{i-1}=0 bi−1=0,令 a i − 1 = 2 ∗ a i a_{i-1}=2*a_i ai−1=2∗ai,否则令 a i − 1 = a i + b i − 1 a_{i-1}=a_i+b_{i-1} ai−1=ai+bi−1。
- 对于剩余所有 j ( j ≠ i , i − 1 ) j(j\ne i,i-1) j(j̸=i,i−1),令 a j = a j + 1 + b j a_j=a_{j+1}+b_j aj=aj+1+bj即可。
- 时间复杂度 O ( N ) O(N) O(N)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 200005; 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, Max, a[MAXN]; long long ans[MAXN]; int main() { read(n); for (int i = 1; i <= n; i++) read(a[i]), chkmax(Max, a[i]); if (Max == 0) { printf("YES\n"); for (int i = 1; i <= n; i++) printf("%d ", 1); printf("\n"); return 0; } a[0] = a[n]; for (int i = 1; i <= n; i++) if (a[i] == Max && a[i - 1] != Max) { long long now = a[i]; int pos = i - 1; ans[i] = now; if (pos == 0) pos = n; while (pos != i) { if (a[pos] == 0 && now == a[i]) now += a[i]; else now += a[pos]; ans[pos] = now; if (--pos == 0) pos = n; } printf("YES\n"); for (int i = 1; i <= n; i++) write(ans[i]), putchar(' '); printf("\n"); return 0; } printf("NO\n"); return 0; }
**【F】**Make Symmetrical
【思路要点】
- 两个关于过原点的某条直线对称的点到原点距离相等。
- 而不定方程 x 2 + y 2 = C ( C ≤ 1 0 12 ) x^2+y^2=C(C≤10^{12}) x2+y2=C(C≤1012)的非负整数解的个数至多有 M = 144 M=144 M=144个。
- 对每个点按照到原点的距离分类,在插入或删除时暴力维护即可。
- 时间复杂度 O ( Q ∗ M ∗ L o g V ) O(Q*M*LogV) O(Q∗M∗LogV)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 100005; 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 {int x, y; }; 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}; } point operator / (point a, int b) {return (point) {a.x / b, a.y / b}; } long long moo(point a) {return a.x * a.x + a.y * a.y; } bool operator < (point a, point b) { if (a.x == b.x) return a.y < b.y; else return a.x < b.x; } int gcd(int x, int y) { if (y == 0) return x; else return gcd(y, x % y); } int n, tot, cnt; map <point, int> ans; map <long long, int> mp; vector <point> a[MAXN]; int main() { read(n); for (int i = 1; i <= n; i++) { int opt; point tmp; read(opt), read(tmp.x), read(tmp.y); if (opt == 1) { cnt++; long long tnp = moo(tmp); if (mp[tnp] == 0) mp[tnp] = ++tot; int pos = mp[tnp]; ans[tmp / gcd(tmp.x, tmp.y)]++; for (unsigned j = 0; j < a[pos].size(); j++) { point sum = tmp + a[pos][j]; sum = sum / gcd(sum.x, sum.y); ans[sum] += 2; } a[pos].push_back(tmp); } if (opt == 2) { cnt--; long long tnp = moo(tmp); if (mp[tnp] == 0) mp[tnp] = ++tot; int pos = mp[tnp]; ans[tmp / gcd(tmp.x, tmp.y)]--; for (unsigned j = 0; j < a[pos].size(); j++) if (tmp.x == a[pos][j].x && tmp.y == a[pos][j].y) { a[pos].erase(a[pos].begin() + j); break; } for (unsigned j = 0; j < a[pos].size(); j++) { point sum = tmp + a[pos][j]; sum = sum / gcd(sum.x, sum.y); ans[sum] -= 2; } } if (opt == 3) writeln(cnt - ans[tmp / gcd(tmp.x, tmp.y)]); } return 0; }
**【G】**Guess the number
【思路要点】
记 d p i , j dp_{i,j} dpi,j表示剩余 i i i次询问的机会,当前确定了 k ≥ j k≥j k≥j的情况下,能够确定的最大长度。
设计出状态之后,是容易转移的:
d p i , j = d p i − 1 , j + f ( i − 1 , i , j + 1 + d p i − 1 , j ) dp_{i,j}=dp_{i-1,j}+f(i-1,i,j+1+dp_{i-1,j}) dpi,j=dpi−1,j+f(i−1,i,j+1+dpi−1,j)
$f(k,i,j)=\left{\begin{array}{rcl}0 & & {i=0}\1+dp_{k,j}+f(k,i-1,j+1+dp_{k,j}) & & {i\ne 0}\end{array} \right. $
并且注意到当 j ≥ 10000 j≥10000 j≥10000, d p i , j = d p i , 10000 dp_{i,j}=dp_{i,10000} dpi,j=dpi,10000,因此我们可以利用该性质对上述过程进行剪枝。
d p 5 , 1 dp_{5,1} dp5,1恰好为$10004205361450474 $,说明题目给出的限制实际上是最紧的。
利用 d p dp dp的结果进行交互即可。
时间复杂度 O ( 5 ∗ 1000 0 2 ) O(5*10000^2) O(5∗100002),实际运行复杂度很不满。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 10005; const int MAXM = 10; const long long INF = 1e18; 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(""); } long long dp[MAXM][MAXN], a[MAXN]; long long times(long long a, long long b) { if ((long double) a * b >= INF) return INF; else return a * b; } int main() { for (int i = 1; i <= 5; i++) for (int j = 1; j <= 10000; j++) { long long ans = -1, pos = j; for (int k = 0; k <= j; k++) { ans += dp[i - 1][pos] + 1; chkmin(ans, INF); pos += dp[i - 1][pos] + 1; if (pos >= 10000) { ans += times(dp[i - 1][10000] + 1, j - k); chkmin(ans, INF); break; } } dp[i][j] = ans; } int cnt = 5; long long pos = 1; while (true) { int tmp = min(pos, 10000ll), tnp = tmp; cout << tmp; a[0] = pos - 1; for (int i = 1; i <= tmp; i++) { pos += dp[cnt - 1][tnp]; tnp = min(tnp + dp[cnt - 1][tnp] + 1, 10000ll); a[i] = pos; pos++; cout << ' ' << a[i]; } cout << endl; read(tnp); if (tnp == -1) return 0; cnt--; pos = a[tnp] + 1; } return 0; }
**【H】**Make Square
【思路要点】
- 首先我们可以将所有数的完全平方因子除去,只剩下每个质因数至多一种。
- 由此我们也可以看出,两种操作实际上是本质相同的,考虑只使用除法操作。
- 将两个数 A , B A,B A,B的乘积修改为完全平方数的过程可以看做对 A , B A,B A,B各进行若干次除法操作,使得它们等于同一个数 C C C。
- 考虑离线询问,从左到右枚举询问的右端点 i i i,并维护数组 M a x C , c n t Max_{C,cnt} MaxC,cnt,表示在前 i − 1 i-1 i−1个数中,进行 c n t cnt cnt次除法操作可以得到 C C C的最靠右的数的位置;再维护数组 r c n t r_{cnt} rcnt表示使得答案为 c n t cnt cnt的最靠右的左端点,显然有了 r r r数组后是容易回答询问的。
- 对于每一个右端点 i i i,考虑 r r r数组的变化,枚举 A i A_i Ai的因数 C C C,再枚举与 A i A_i Ai配为完全平方数的 B B B得到 C C C的操作次数,即可借助 M a x Max Max数组更新 r r r数组,对于 M a x Max Max数组的更新类似。
- 时间复杂度 O ( N ∗ A i ∗ M a x A n s + Q ∗ M a x A n s ) O(N*\sqrt{A_i}*MaxAns+Q*MaxAns) O(N∗Ai∗MaxAns+Q∗MaxAns),其中 M a x A n s = 11 MaxAns=11 MaxAns=11。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e5 + 5; const int MAXQ = 2e6 + 5; const int MAXV = 6e6 + 5; const int Maxans = 12; 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, q, a[MAXN], ans[MAXQ]; int r[Maxans + 5], Max[MAXV][Maxans + 5]; int tot, prime[MAXV], f[MAXV], cnt[MAXV]; vector <int> pos[MAXN], home[MAXN]; void init() { for (int i = 2; i < MAXV; i++) { if (f[i] == 0) prime[++tot] = f[i] = i, cnt[i] = 1; for (unsigned j = 1; j <= tot && prime[j] <= f[i]; j++) { int tmp = prime[j] * i; if (tmp >= MAXV) break; f[tmp] = prime[j]; cnt[tmp] = cnt[i] + 1; } } } int process(int x) { int ans = 1; for (int i = 1; prime[i] * prime[i] <= x; i++) if (x % prime[i] == 0) { int cnt = 0; while (x % prime[i] == 0) x /= prime[i], cnt++; if (cnt & 1) ans *= prime[i]; } return ans * x; } int main() { read(n), read(q); init(); for (int i = 1; i <= n; i++) { read(a[i]); a[i] = process(a[i]); } for (int i = 1; i <= q; i++) { int x, y; read(x), read(y); pos[y].push_back(x); home[y].push_back(i); } for (int i = 1; i <= n; i++) { for (int j = 1; j * j <= a[i]; j++) { if (a[i] % j == 0) { int now = cnt[a[i] / j]; for (int k = 0; k + now <= Maxans; k++) chkmax(r[k + now], Max[j][k]); now = cnt[j]; for (int k = 0; k + now <= Maxans; k++) chkmax(r[k + now], Max[a[i] / j][k]); } } for (int j = 1; j * j <= a[i]; j++) { if (a[i] % j == 0) { chkmax(Max[j][cnt[a[i] / j]], i); chkmax(Max[a[i] / j][cnt[j]], i); } } for (unsigned j = 0; j < pos[i].size(); j++) { int tans = Maxans; for (int k = 0; k <= Maxans; k++) if (r[k] >= pos[i][j]) chkmin(tans, k); ans[home[i][j]] = tans; } } for (int i = 1; i <= q; i++) writeln(ans[i]); return 0; }