版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39972971/article/details/82837083
【比赛链接】
【题解链接】
**【A】**Enlarge GCD
【思路要点】
- 令所有数的 为 ,将所有数除去 ,问题变为了所有数的 为 的情况。
- 我们要选出一个最大的数集,使得这个数集中的数存在不为 的公因数。
- 显然我们只需要考虑所有质数即可,线性筛求出每个数的最小质因子,质因数分解每一个数即可。
- 时间复杂度 。
【代码】
#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
【思路要点】
打表题。- 不妨令 。
- 用最大流求出 较小的时候的答案,我们发现当 足够大时,答案为 ,特殊处理 的情况即可。
或者你也可以直接交一张表。- 时间复杂度 。
【代码】
#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
【思路要点】
- 令 号节点为根,节点 的子树权值和为 。
- 若给定 ,一棵树能够被分成权值和相同的 部分的充要条件为 为整数,并且存在 个子树 满足 是 的整数倍。
- 因此,我们可以先计算出对于每一个 ,满足 是 的整数倍的子树 的个数 ,然后,在合法的 上简单 ,得到答案。
- 考虑一个子树 对 的贡献,由于 ,因此 ,即 是 的倍数,令 加 ,那么 ,简单计算 即可。
- 时间复杂度 。
【代码】
#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
【思路要点】
- 考虑如何计算 的权值,从左向右考虑每一个右端点 ,将 赋值为 ,那么 的权值即为 的位置的个数。
- 对于一个右端点 ,我们希望维护所有左端点 对应的 的权值。在将 赋值为 时,若有 个位置被由 赋值为了 ,那么 中的 对应的 的权值都会增加 。
- 用 存储染色情况,记录每一个 处答案的增量。
- 二分答案 ,用 结合前文计算出的答案的增量计算权值大于等于 的方案数,得到权值第 大的方案 。
- 再计算一下权值大于等于 的方案和即可。
- 时间复杂度 。
【代码】
#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
【思路要点】
- 令 表示 二进制表示中 的个数。
- 令 , 。
- 计算 ,那么 。
- 时间复杂度 。
- 可以看一下题解中出题人是如何得到这个做法的,很有启发意义。
【代码】
#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; }