【比赛链接】
【题解链接】
**【A】**Enlarge GCD
【思路要点】
- 令所有数的 g c d gcd gcd 为 g g g ,将所有数除去 g g g ,问题变为了所有数的 g c d gcd gcd 为 1 1 1 的情况。
- 我们要选出一个最大的数集,使得这个数集中的数存在不为 1 1 1 的公因数。
- 显然我们只需要考虑所有质数即可,线性筛求出每个数的最小质因子,质因数分解每一个数即可。
- 时间复杂度 O ( N L o g V + V ) O(NLogV+V) O(NLogV+V) 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 3e5 + 5; const int MAXV = 1.5e7 + 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, tot, a[MAXN], prime[MAXV], f[MAXV], cnt[MAXV]; void init(int n) { for (int i = 2; i <= n; i++) { if (f[i] == 0) prime[++tot] = f[i] = i; for (int j = 1; j <= tot && prime[j] <= f[i]; j++) { int tmp = prime[j] * i; if (tmp > n) break; f[tmp] = prime[j]; } } } int gcd(int x, int y) { if (y == 0) return x; else return gcd(y, x % y); } int main() { init(1.5e7); int g = 0; read(n); for (int i = 1; i <= n; i++) { read(a[i]); g = gcd(a[i], g); } int ans = 0; for (int i = 1; i <= n; i++) { int tmp = a[i] / g; while (tmp != 1) { cnt[f[tmp]]++; if (cnt[f[tmp]] > ans) ans = cnt[f[tmp]]; int tnp = f[tmp]; while (tmp % tnp == 0) tmp /= tnp; } } if (ans == 0) printf("-1\n"); else printf("%d\n", n - ans); return 0; }
**【B】**Little C Loves 3 II
【思路要点】
打表题。- 不妨令 N ≤ M N≤M N≤M 。
- 用最大流求出 N , M N,M N,M 较小的时候的答案,我们发现当 N , M N,M N,M 足够大时,答案为 2 ∗ ⌊ N ∗ M 2 ⌋ 2*\lfloor\frac{N*M}{2}\rfloor 2∗⌊2N∗M⌋ ,特殊处理 N = 1 N=1 N=1 的情况即可。
或者你也可以直接交一张表。- 时间复杂度 O ( 1 ) O(1) O(1) 。
【代码】
#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 main() { ll n, m; read(n), read(m); if (n > m) swap(n, m); if (n == 1) { ll ans = m / 6 * 6; ll tmp = m % 6; ans += max(0ll, tmp - 3) * 2; writeln(ans); } else if (n == 2) { ll ans = n * m; if (m == 2) ans -= 4; if (m == 3) ans -= 2; if (m == 7) ans -= 2; writeln(ans); } else writeln(n * m - n * m % 2); return 0; }
**【C】**Region Separation
【思路要点】
- 令 1 1 1 号节点为根,节点 i i i 的子树权值和为 s u m i sum_i sumi 。
- 若给定 k k k ,一棵树能够被分成权值和相同的 k k k 部分的充要条件为 s u m 1 k \frac{sum_1}{k} ksum1 为整数,并且存在 k k k 个子树 i i i 满足 s u m i sum_i sumi 是 s u m 1 k \frac{sum_1}{k} ksum1 的整数倍。
- 因此,我们可以先计算出对于每一个 k k k ,满足 s u m i sum_i sumi 是 s u m 1 k \frac{sum_1}{k} ksum1 的整数倍的子树 i i i 的个数 c n t k cnt_k cntk ,然后,在合法的 k k k 上简单 d p dp dp ,得到答案。
- 考虑一个子树 i i i 对 c n t k cnt_k cntk 的贡献,由于 s u m i = t ∗ s u m 1 k ( t ∈ Z ) sum_i=\frac{t*sum_1}{k}\ (t\in \Z) sumi=kt∗sum1 (t∈Z) ,因此 k = t ∗ s u m 1 s u m i ( t ∈ Z ) k=\frac{t*sum_1}{sum_{i}}\ (t\in \Z) k=sumit∗sum1 (t∈Z) ,即 k k k 是 t m p = s u m 1 g c d ( s u m 1 , s u m i ) tmp=\frac{sum_1}{gcd(sum_1,sum_i)} tmp=gcd(sum1,sumi)sum1 的倍数,令 f t m p f_{tmp} ftmp 加 1 1 1 ,那么 c n t k = ∑ j / k f j cnt_k=\sum_{j/k}f_j cntk=∑j/kfj ,简单计算 c n t k cnt_k cntk 即可。
- 时间复杂度 O ( N L o g N + N L o g V ) O(NLogN+NLogV) O(NLogN+NLogV) 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 1e6 + 5; const int P = 1e9 + 7; 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); } int n, p[MAXN], cnt[MAXN], dp[MAXN]; ll a[MAXN]; void update(int &x, int y) { x += y; if (x >= P) x -= P; } int main() { read(n); for (int i = 1; i <= n; i++) read(a[i]); for (int i = 2; i <= n; i++) read(p[i]); for (int i = n; i >= 2; i--) a[p[i]] += a[i]; for (int i = 1; i <= n; i++) { ll tmp = a[1] / gcd(a[i], a[1]); if (tmp <= n) cnt[tmp]++; } for (int i = n; i >= 1; i--) for (int j = 2 * i; j <= n; j += i) cnt[j] += cnt[i]; int ans = 0; dp[1] = 1; for (int i = 1; i <= n; i++) { if (cnt[i] != i) continue; update(ans, dp[i]); for (int j = 2 * i; j <= n; j += i) if (cnt[j] == j) update(dp[j], dp[i]); } writeln(ans); return 0; }
**【D】**Intervals of Intervals
【思路要点】
- 考虑如何计算 [ l , r ] [l,r] [l,r] 的权值,从左向右考虑每一个右端点 r r r ,将 [ a r , b r ] [a_r,b_r] [ar,br] 赋值为 r r r ,那么 [ l , r ] [l,r] [l,r] 的权值即为 ≥ l ≥l ≥l 的位置的个数。
- 对于一个右端点 r r r ,我们希望维护所有左端点 l l l 对应的 [ l , r ] [l,r] [l,r] 的权值。在将 [ a r , b r ] [a_r,b_r] [ar,br] 赋值为 r r r 时,若有 x x x 个位置被由 f o r m e r former former 赋值为了 r r r ,那么 ( f o r m e r , r ] (former,r] (former,r] 中的 l l l 对应的 [ l , r ] [l,r] [l,r] 的权值都会增加 x x x 。
- 用 s e t set set 存储染色情况,记录每一个 r r r 处答案的增量。
- 二分答案 m i d mid mid ,用 T w o P o i n t e r s TwoPointers TwoPointers 结合前文计算出的答案的增量计算权值大于等于 m i d mid mid 的方案数,得到权值第 k k k 大的方案 v a l k valk valk 。
- 再计算一下权值大于等于 v a l k + 1 valk+1 valk+1 的方案和即可。
- 时间复杂度 O ( N L o g N + N L o g V ) O(NLogN+NLogV) O(NLogN+NLogV) 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 3e5 + 5; const int INF = 1e9; 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(""); } struct info {int l, r, val; }; bool operator < (info a, info b) {return a.l < b.l; } multiset <info> st; int n, k, l[MAXN], r[MAXN]; vector <pair <int, int> > a[MAXN]; int cnt(int mid) { static int add[MAXN]; memset(add, 0, sizeof(add)); int ans = 0, l = 1, now = 0; for (int i = 1; i <= n; i++) { unsigned j = 0; while (j < a[i].size() && a[i][j].first <= l) now += a[i][j++].second; while (l <= i && now >= mid) { now += add[++l]; while (j < a[i].size() && a[i][j].first <= l) now += a[i][j++].second; } ans += l - 1; while (j < a[i].size()) add[a[i][j].first] += a[i][j].second, j++; if (ans > INF) return INF; } return ans; } ll sum(int mid) { static int add[MAXN]; memset(add, 0, sizeof(add)); ll ans = 0; int l = 1, now = 0; for (int i = 1; i <= n; i++) { unsigned j = 0; while (j < a[i].size() && a[i][j].first <= l) { ans += (n - i + 1ll) * (l - a[i][j].first + 0ll) * a[i][j].second; now += a[i][j++].second; } while (l <= i && now >= mid) { ans += now * (n - i + 1ll); now += add[++l]; while (j < a[i].size() && a[i][j].first <= l) now += a[i][j++].second; } while (j < a[i].size()) add[a[i][j].first] += a[i][j].second, j++; } return ans; } int main() { read(n), read(k); for (int i = 1; i <= n; i++) read(l[i]), read(r[i]), r[i]--; st.insert((info) {1, INF, 0}); for (int i = 1; i <= n; i++) { int ql = l[i], qr = r[i], lv = i, rv = i; info now = (info) {l[i], r[i], i}; multiset <info> :: iterator tmp = st.upper_bound(now); if (--tmp != st.end()) { ql = (*tmp).l; lv = (*tmp).val; } while (tmp != st.end() && (*tmp).l <= r[i]) { if ((*tmp).r >= r[i]) { qr = (*tmp).r; rv = (*tmp).val; } multiset <info> :: iterator tnp = tmp; a[i].push_back(make_pair((*tmp).val + 1, (*tmp).r - (*tmp).l + 1)); tmp++; st.erase(tnp); } if (ql != l[i]) { st.insert((info) {ql, l[i] - 1, lv}); a[i].push_back(make_pair(lv + 1, -(l[i] - ql))); } if (qr != r[i]) { st.insert((info) {r[i] + 1, qr, rv}); a[i].push_back(make_pair(rv + 1, -(qr - r[i]))); } st.insert((info) {l[i], r[i], i}); a[i].push_back(make_pair(i + 1, -(r[i] - l[i] + 1))); sort(a[i].begin(), a[i].end()); } int l = 1, r = 1e9; while (l < r) { int mid = (l + r + 1) / 2; if (cnt(mid) >= k) l = mid; else r = mid - 1; } ll ans = 1ll * l * (k - cnt(l + 1)); writeln(ans + sum(l + 1)); return 0; }
**【E】**Little C Loves 3 III
【思路要点】
- 令 f ( i ) f(i) f(i) 表示 i i i 二进制表示中 1 1 1 的个数。
- 令 A i = a i ∗ 4 f ( i ) A_i=a_i*4^{f(i)} Ai=ai∗4f(i) , B i = b i ∗ 4 f ( i ) B_i=b_i*4^{f(i)} Bi=bi∗4f(i) 。
- 计算 C i = ∑ j ∣ k = i A j ∗ B k C_i=\sum_{j|k=i}A_j*B_k Ci=∑j∣k=iAj∗Bk ,那么 c i = C i f ( i ) % 4 c_i=\frac{C_i}{f(i)}\%\ 4 ci=f(i)Ci% 4 。
- 时间复杂度 O ( 2 N ∗ N ) O(2^N*N) O(2N∗N) 。
- 可以看一下题解中出题人是如何得到这个做法的,很有启发意义。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = (1 << 21) + 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, Log, bits[MAXN]; char s[MAXN], t[MAXN]; ll a[MAXN], b[MAXN], c[MAXN], P; void FWT(ll *a) { for (int bit = 1; bit <= Log; bit++) for (int i = 0; i < n; i += 1 << bit) { for (int j = i, k = i + (1 << (bit - 1)); k < i + (1 << bit); j++, k++) { a[k] += a[j]; if (a[k] >= P) a[k] -= P; } } } void UFWT(ll *a) { for (int bit = 1; bit <= Log; bit++) for (int i = 0; i < n; i += 1 << bit) { for (int j = i, k = i + (1 << (bit - 1)); k < i + (1 << bit); j++, k++) { a[k] -= a[j]; if (a[k] < 0) a[k] += P; } } } int main() { read(Log), n = 1 << Log; P = 1ll << (2 * Log + 2); scanf("\n%s", s); scanf("\n%s", t); for (int i = 0; i < n; i++) { if (i != 0) bits[i] = bits[i - (i & -i)] + 1; a[i] = (s[i] - '0' + 0ll) << (bits[i] * 2); b[i] = (t[i] - '0' + 0ll) << (bits[i] * 2); } FWT(a), FWT(b); for (int i = 0; i < n; i++) { ll tmp = (ld) a[i] * b[i] / P; c[i] = ((a[i] * b[i] - tmp * P) % P + P) % P; } UFWT(c); for (int i = 0; i < n; i++) { c[i] >>= (bits[i] * 2); putchar((c[i] & 3) + '0'); } return 0; }