A – Enlarge GCD
大概可以想到,先把所有的数除掉一个 GCD 之后再来计算出现次数最多的素因子。我们用埃筛筛一下每个数的最小素因子,然后暴力除做标记即可。
// CF1034A.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 3e5 + 200, MAX_P = 1.5e7 + 200;
int n, ai[MAX_N], primes[MAX_P], ptot, gloabl_gcd, cnt[MAX_P], sfactor[MAX_P];
bool vis[MAX_P];
int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }
int main()
{
// sieve process;
for (int i = 2; i < MAX_P; i++)
{
if (!vis[i])
primes[++ptot] = i;
for (int j = 1; j <= ptot && 1LL * primes[j] * i < MAX_P; j++)
{
vis[i * primes[j]] = true;
if (i % primes[j] == 0)
break;
}
}
for (int i = 2; i < MAX_P; i++)
sfactor[i] = i;
for (int i = 2; i < MAX_P; i++)
if (!vis[i])
for (int j = (i << 1); j < MAX_P; j += i)
if (sfactor[j] == j)
sfactor[j] = i;
// read;
scanf("%d%d", &n, &gloabl_gcd), ai[1] = gloabl_gcd;
for (int i = 2; i <= n; i++)
scanf("%d", &ai[i]), gloabl_gcd = gcd(gloabl_gcd, ai[i]);
for (int i = 1; i <= n; i++)
ai[i] /= gloabl_gcd;
for (int i = 1; i <= n; i++)
{
int curt = ai[i];
while (curt != 1)
{
int x = sfactor[curt];
cnt[x]++;
while (sfactor[curt] == x)
curt /= sfactor[curt];
}
}
int ans = n;
for (int i = 1; i < MAX_P; i++)
ans = min(ans, n - cnt[i]);
if (ans == n)
puts("-1");
else
printf("%d\n", ans);
return 0;
}
// CF1034A.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 3e5 + 200, MAX_P = 1.5e7 + 200;
int n, ai[MAX_N], primes[MAX_P], ptot, gloabl_gcd, cnt[MAX_P], sfactor[MAX_P];
bool vis[MAX_P];
int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }
int main()
{
// sieve process;
for (int i = 2; i < MAX_P; i++)
{
if (!vis[i])
primes[++ptot] = i;
for (int j = 1; j <= ptot && 1LL * primes[j] * i < MAX_P; j++)
{
vis[i * primes[j]] = true;
if (i % primes[j] == 0)
break;
}
}
for (int i = 2; i < MAX_P; i++)
sfactor[i] = i;
for (int i = 2; i < MAX_P; i++)
if (!vis[i])
for (int j = (i << 1); j < MAX_P; j += i)
if (sfactor[j] == j)
sfactor[j] = i;
// read;
scanf("%d%d", &n, &gloabl_gcd), ai[1] = gloabl_gcd;
for (int i = 2; i <= n; i++)
scanf("%d", &ai[i]), gloabl_gcd = gcd(gloabl_gcd, ai[i]);
for (int i = 1; i <= n; i++)
ai[i] /= gloabl_gcd;
for (int i = 1; i <= n; i++)
{
int curt = ai[i];
while (curt != 1)
{
int x = sfactor[curt];
cnt[x]++;
while (sfactor[curt] == x)
curt /= sfactor[curt];
}
}
int ans = n;
for (int i = 1; i < MAX_P; i++)
ans = min(ans, n - cnt[i]);
if (ans == n)
puts("-1");
else
printf("%d\n", ans);
return 0;
}
// CF1034A.cpp #include <bits/stdc++.h> using namespace std; const int MAX_N = 3e5 + 200, MAX_P = 1.5e7 + 200; int n, ai[MAX_N], primes[MAX_P], ptot, gloabl_gcd, cnt[MAX_P], sfactor[MAX_P]; bool vis[MAX_P]; int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); } int main() { // sieve process; for (int i = 2; i < MAX_P; i++) { if (!vis[i]) primes[++ptot] = i; for (int j = 1; j <= ptot && 1LL * primes[j] * i < MAX_P; j++) { vis[i * primes[j]] = true; if (i % primes[j] == 0) break; } } for (int i = 2; i < MAX_P; i++) sfactor[i] = i; for (int i = 2; i < MAX_P; i++) if (!vis[i]) for (int j = (i << 1); j < MAX_P; j += i) if (sfactor[j] == j) sfactor[j] = i; // read; scanf("%d%d", &n, &gloabl_gcd), ai[1] = gloabl_gcd; for (int i = 2; i <= n; i++) scanf("%d", &ai[i]), gloabl_gcd = gcd(gloabl_gcd, ai[i]); for (int i = 1; i <= n; i++) ai[i] /= gloabl_gcd; for (int i = 1; i <= n; i++) { int curt = ai[i]; while (curt != 1) { int x = sfactor[curt]; cnt[x]++; while (sfactor[curt] == x) curt /= sfactor[curt]; } } int ans = n; for (int i = 1; i < MAX_P; i++) ans = min(ans, n - cnt[i]); if (ans == n) puts("-1"); else printf("%d\n", ans); return 0; }
B – Little C Loves 3 II
打表题吧,先用网络流打个表然后找到规律即可。
// CF1034B.cpp
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n, m;
int main()
{
scanf("%lld%lld", &n, &m);
if (n > m)
swap(n, m);
long long ans = 0;
if (n == 1)
ans = 6LL * ll(m / 6) + 2LL * max(m % 6 - 3, 0LL);
else if (n == 2)
{
if (m == 2)
ans = 0;
else if (m == 3)
ans = 4;
else if (m == 7)
ans = 12;
else
ans = m * n;
}
else
ans = 1LL * n * m;
printf("%lld\t", ans ^ (ans & 1LL));
return 0;
}
// CF1034B.cpp
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n, m;
int main()
{
scanf("%lld%lld", &n, &m);
if (n > m)
swap(n, m);
long long ans = 0;
if (n == 1)
ans = 6LL * ll(m / 6) + 2LL * max(m % 6 - 3, 0LL);
else if (n == 2)
{
if (m == 2)
ans = 0;
else if (m == 3)
ans = 4;
else if (m == 7)
ans = 12;
else
ans = m * n;
}
else
ans = 1LL * n * m;
printf("%lld\t", ans ^ (ans & 1LL));
return 0;
}
// CF1034B.cpp #include <bits/stdc++.h> using namespace std; typedef long long ll; ll n, m; int main() { scanf("%lld%lld", &n, &m); if (n > m) swap(n, m); long long ans = 0; if (n == 1) ans = 6LL * ll(m / 6) + 2LL * max(m % 6 - 3, 0LL); else if (n == 2) { if (m == 2) ans = 0; else if (m == 3) ans = 4; else if (m == 7) ans = 12; else ans = m * n; } else ans = 1LL * n * m; printf("%lld\t", ans ^ (ans & 1LL)); return 0; }
C – Region Seperation
啊这题好难。
首先考虑分成二级的情况,列出几个已知的东西:
- 二级的和 \(L_2\) 一定为 \(S_1\) 的一个约数。
- 假设要剥离出 \(\frac{S_1}{k}\) 大小的块,那么我们就可以自底向上的去做,然后记录下与 \(S_1\) 的 gcd。考虑每一个 \(d = \gcd\) 出现了 \(d\) 次,那么在模意义下可以直接相减 \(k\) 次获得至少 \(k\) 组个为 \(0\) 的差。这个跟某个背包题的贪心结论很相似。
- 最后用埃筛式的 DP 来统计所有的答案即可。
// CF1034C.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 1e6 + 200, mod = 1e9 + 7;
typedef long long ll;
int n, fa[MAX_N], dp[MAX_N];
ll sum[MAX_N], cnt[MAX_N];
ll gcd(ll a, ll b) { return b == 0 ? a : gcd(b, a % b); }
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%lld", &sum[i]);
for (int i = 2; i <= n; i++)
scanf("%d", &fa[i]);
for (int i = n; i >= 1; i--)
if (fa[i])
sum[fa[i]] += sum[i];
for (int i = n; i >= 1; i--)
{
sum[i] = sum[1] / gcd(sum[1], sum[i]);
if (sum[i] <= n)
cnt[sum[i]]++;
}
for (int i = n; i >= 1; i--)
for (int j = (i << 1); j <= n; j += i)
cnt[j] += cnt[i];
dp[1] = 1;
int ans = 0;
for (int i = 1; i <= n; i++)
if (cnt[i] == i)
{
ans = (0LL + ans + dp[i]) % mod;
for (int j = i << 1; j <= n; j += i)
dp[j] = (0LL + dp[j] + dp[i]) % mod;
}
printf("%d\n", ans);
return 0;
}
// CF1034C.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 1e6 + 200, mod = 1e9 + 7;
typedef long long ll;
int n, fa[MAX_N], dp[MAX_N];
ll sum[MAX_N], cnt[MAX_N];
ll gcd(ll a, ll b) { return b == 0 ? a : gcd(b, a % b); }
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%lld", &sum[i]);
for (int i = 2; i <= n; i++)
scanf("%d", &fa[i]);
for (int i = n; i >= 1; i--)
if (fa[i])
sum[fa[i]] += sum[i];
for (int i = n; i >= 1; i--)
{
sum[i] = sum[1] / gcd(sum[1], sum[i]);
if (sum[i] <= n)
cnt[sum[i]]++;
}
for (int i = n; i >= 1; i--)
for (int j = (i << 1); j <= n; j += i)
cnt[j] += cnt[i];
dp[1] = 1;
int ans = 0;
for (int i = 1; i <= n; i++)
if (cnt[i] == i)
{
ans = (0LL + ans + dp[i]) % mod;
for (int j = i << 1; j <= n; j += i)
dp[j] = (0LL + dp[j] + dp[i]) % mod;
}
printf("%d\n", ans);
return 0;
}
// CF1034C.cpp #include <bits/stdc++.h> using namespace std; const int MAX_N = 1e6 + 200, mod = 1e9 + 7; typedef long long ll; int n, fa[MAX_N], dp[MAX_N]; ll sum[MAX_N], cnt[MAX_N]; ll gcd(ll a, ll b) { return b == 0 ? a : gcd(b, a % b); } int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%lld", &sum[i]); for (int i = 2; i <= n; i++) scanf("%d", &fa[i]); for (int i = n; i >= 1; i--) if (fa[i]) sum[fa[i]] += sum[i]; for (int i = n; i >= 1; i--) { sum[i] = sum[1] / gcd(sum[1], sum[i]); if (sum[i] <= n) cnt[sum[i]]++; } for (int i = n; i >= 1; i--) for (int j = (i << 1); j <= n; j += i) cnt[j] += cnt[i]; dp[1] = 1; int ans = 0; for (int i = 1; i <= n; i++) if (cnt[i] == i) { ans = (0LL + ans + dp[i]) % mod; for (int j = i << 1; j <= n; j += i) dp[j] = (0LL + dp[j] + dp[i]) % mod; } printf("%d\n", ans); return 0; }
D – Intervals of Intervals
先来一个二分答案,找出第 \(k\) 大的集合大小。我们可以用 ODT 的方式来处理线段和。
二分答案之后,我们可以使用这个答案来作为 two-pointers 的条件,然后就可以算个数和长度和。最后答案会多出一点,我们只需要用被计算的线段的个数减掉要求的线段个数,再乘上第 \(k\) 大的集合大小即可算完。
// CF1034D.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 3e5 + 200, INF = 0x3f3f3f3f;
typedef pair<int, int> pii;
typedef long long ll;
int n, limit, li[MAX_N], ri[MAX_N], len[MAX_N];
struct node
{
int l, r, timestamp;
bool operator<(const node &rhs) const { return l < rhs.l || (l == rhs.l && r < rhs.r); }
};
multiset<node> ms;
vector<pii> info[MAX_N];
bool check(int mid)
{
int acc = 0;
ll cnt = 0;
for (int i = 1, ptr = 1; i <= n; i++)
{
len[i] = ri[i] - li[i] + 1, acc += len[i];
for (auto &x : info[i])
{
len[x.second] -= x.first;
// coming later;
if (x.second >= ptr)
acc -= x.first;
}
while (acc >= mid)
acc -= len[ptr], ptr++;
cnt += ptr - 1;
}
return cnt >= limit;
}
void calc(int max_len)
{
int acc = 0;
ll ans = 0, len_acc = 0, len_cnt = 0;
for (int i = 1, ptr = 1; i <= n; i++)
{
len[i] = ri[i] - li[i] + 1, acc += len[i];
for (auto &x : info[i])
{
len[x.second] -= x.first;
// coming later;
if (x.second >= ptr)
acc -= x.first;
if (x.second > 0 && x.second < ptr)
len_acc -= 1LL * x.second * x.first;
}
while (acc >= max_len)
acc -= len[ptr], len_acc += 1LL * ptr * len[ptr], ptr++;
ans += len_acc + 1LL * acc * (ptr - 1), len_cnt += (ptr - 1);
}
printf("%lld\n", ans - 1LL * (len_cnt - limit) * max_len);
}
int main()
{
scanf("%d%d", &n, &limit);
for (int i = 1; i <= n; i++)
scanf("%d%d", &li[i], &ri[i]), ri[i]--;
// ODT-like initialize;
ms.insert(node{0, INF, 0});
for (int i = 1; i <= n; i++)
{
auto it = --ms.lower_bound(node{li[i], INF, 0});
if (it->r >= ri[i])
{
// covered;
info[i].push_back(make_pair(ri[i] - li[i] + 1, it->timestamp));
if (it->l < li[i])
ms.insert(node{it->l, li[i] - 1, it->timestamp});
if (it->r > ri[i])
ms.insert(node{ri[i] + 1, it->r, it->timestamp});
ms.erase(it), ms.insert(node{li[i], ri[i], i});
}
else
{
// intersect;
info[i].push_back(make_pair(it->r - li[i] + 1, it->timestamp));
if (it->l < li[i])
ms.insert(node{it->l, li[i] - 1, it->timestamp});
ms.erase(it++);
if (it->r < li[i])
it++;
while (it->r < ri[i])
info[i].push_back(make_pair(it->r - it->l + 1, it->timestamp)), ms.erase(it++);
info[i].push_back(make_pair(ri[i] - it->l + 1, it->timestamp));
if (it->r > ri[i])
ms.insert(node{ri[i] + 1, it->r, it->timestamp});
ms.erase(it), ms.insert(node{li[i], ri[i], i});
}
}
int l = 0, r = 1e9, res;
while (l <= r)
{
int mid = (l + r) >> 1;
if (check(mid))
l = mid + 1, res = mid;
else
r = mid - 1;
}
calc(res);
return 0;
}
// CF1034D.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 3e5 + 200, INF = 0x3f3f3f3f;
typedef pair<int, int> pii;
typedef long long ll;
int n, limit, li[MAX_N], ri[MAX_N], len[MAX_N];
struct node
{
int l, r, timestamp;
bool operator<(const node &rhs) const { return l < rhs.l || (l == rhs.l && r < rhs.r); }
};
multiset<node> ms;
vector<pii> info[MAX_N];
bool check(int mid)
{
int acc = 0;
ll cnt = 0;
for (int i = 1, ptr = 1; i <= n; i++)
{
len[i] = ri[i] - li[i] + 1, acc += len[i];
for (auto &x : info[i])
{
len[x.second] -= x.first;
// coming later;
if (x.second >= ptr)
acc -= x.first;
}
while (acc >= mid)
acc -= len[ptr], ptr++;
cnt += ptr - 1;
}
return cnt >= limit;
}
void calc(int max_len)
{
int acc = 0;
ll ans = 0, len_acc = 0, len_cnt = 0;
for (int i = 1, ptr = 1; i <= n; i++)
{
len[i] = ri[i] - li[i] + 1, acc += len[i];
for (auto &x : info[i])
{
len[x.second] -= x.first;
// coming later;
if (x.second >= ptr)
acc -= x.first;
if (x.second > 0 && x.second < ptr)
len_acc -= 1LL * x.second * x.first;
}
while (acc >= max_len)
acc -= len[ptr], len_acc += 1LL * ptr * len[ptr], ptr++;
ans += len_acc + 1LL * acc * (ptr - 1), len_cnt += (ptr - 1);
}
printf("%lld\n", ans - 1LL * (len_cnt - limit) * max_len);
}
int main()
{
scanf("%d%d", &n, &limit);
for (int i = 1; i <= n; i++)
scanf("%d%d", &li[i], &ri[i]), ri[i]--;
// ODT-like initialize;
ms.insert(node{0, INF, 0});
for (int i = 1; i <= n; i++)
{
auto it = --ms.lower_bound(node{li[i], INF, 0});
if (it->r >= ri[i])
{
// covered;
info[i].push_back(make_pair(ri[i] - li[i] + 1, it->timestamp));
if (it->l < li[i])
ms.insert(node{it->l, li[i] - 1, it->timestamp});
if (it->r > ri[i])
ms.insert(node{ri[i] + 1, it->r, it->timestamp});
ms.erase(it), ms.insert(node{li[i], ri[i], i});
}
else
{
// intersect;
info[i].push_back(make_pair(it->r - li[i] + 1, it->timestamp));
if (it->l < li[i])
ms.insert(node{it->l, li[i] - 1, it->timestamp});
ms.erase(it++);
if (it->r < li[i])
it++;
while (it->r < ri[i])
info[i].push_back(make_pair(it->r - it->l + 1, it->timestamp)), ms.erase(it++);
info[i].push_back(make_pair(ri[i] - it->l + 1, it->timestamp));
if (it->r > ri[i])
ms.insert(node{ri[i] + 1, it->r, it->timestamp});
ms.erase(it), ms.insert(node{li[i], ri[i], i});
}
}
int l = 0, r = 1e9, res;
while (l <= r)
{
int mid = (l + r) >> 1;
if (check(mid))
l = mid + 1, res = mid;
else
r = mid - 1;
}
calc(res);
return 0;
}
// CF1034D.cpp #include <bits/stdc++.h> using namespace std; const int MAX_N = 3e5 + 200, INF = 0x3f3f3f3f; typedef pair<int, int> pii; typedef long long ll; int n, limit, li[MAX_N], ri[MAX_N], len[MAX_N]; struct node { int l, r, timestamp; bool operator<(const node &rhs) const { return l < rhs.l || (l == rhs.l && r < rhs.r); } }; multiset<node> ms; vector<pii> info[MAX_N]; bool check(int mid) { int acc = 0; ll cnt = 0; for (int i = 1, ptr = 1; i <= n; i++) { len[i] = ri[i] - li[i] + 1, acc += len[i]; for (auto &x : info[i]) { len[x.second] -= x.first; // coming later; if (x.second >= ptr) acc -= x.first; } while (acc >= mid) acc -= len[ptr], ptr++; cnt += ptr - 1; } return cnt >= limit; } void calc(int max_len) { int acc = 0; ll ans = 0, len_acc = 0, len_cnt = 0; for (int i = 1, ptr = 1; i <= n; i++) { len[i] = ri[i] - li[i] + 1, acc += len[i]; for (auto &x : info[i]) { len[x.second] -= x.first; // coming later; if (x.second >= ptr) acc -= x.first; if (x.second > 0 && x.second < ptr) len_acc -= 1LL * x.second * x.first; } while (acc >= max_len) acc -= len[ptr], len_acc += 1LL * ptr * len[ptr], ptr++; ans += len_acc + 1LL * acc * (ptr - 1), len_cnt += (ptr - 1); } printf("%lld\n", ans - 1LL * (len_cnt - limit) * max_len); } int main() { scanf("%d%d", &n, &limit); for (int i = 1; i <= n; i++) scanf("%d%d", &li[i], &ri[i]), ri[i]--; // ODT-like initialize; ms.insert(node{0, INF, 0}); for (int i = 1; i <= n; i++) { auto it = --ms.lower_bound(node{li[i], INF, 0}); if (it->r >= ri[i]) { // covered; info[i].push_back(make_pair(ri[i] - li[i] + 1, it->timestamp)); if (it->l < li[i]) ms.insert(node{it->l, li[i] - 1, it->timestamp}); if (it->r > ri[i]) ms.insert(node{ri[i] + 1, it->r, it->timestamp}); ms.erase(it), ms.insert(node{li[i], ri[i], i}); } else { // intersect; info[i].push_back(make_pair(it->r - li[i] + 1, it->timestamp)); if (it->l < li[i]) ms.insert(node{it->l, li[i] - 1, it->timestamp}); ms.erase(it++); if (it->r < li[i]) it++; while (it->r < ri[i]) info[i].push_back(make_pair(it->r - it->l + 1, it->timestamp)), ms.erase(it++); info[i].push_back(make_pair(ri[i] - it->l + 1, it->timestamp)); if (it->r > ri[i]) ms.insert(node{ri[i] + 1, it->r, it->timestamp}); ms.erase(it), ms.insert(node{li[i], ri[i], i}); } } int l = 0, r = 1e9, res; while (l <= r) { int mid = (l + r) >> 1; if (check(mid)) l = mid + 1, res = mid; else r = mid - 1; } calc(res); return 0; }