文章目录
传送门
A.TubeTube Feed(贪心)
题意:要求在 t 时间内看完某个节目,每次换台需要 1 单位时间,给出对应的乐子程度,求出最多的乐子度,而且只能看一个节目。
思路:我们预处理一下看完每个节目的最小时间,最小时间是节目时长+换台次数,然后O(n)贪心一次即可。
#include <bits/stdc++.h>
#define ll long long
#define ls (id << 1)
#define rs (id << 1 | 1)
using namespace std;
const int N = 105;
int a[N], b[N];
void solve() {
int n, t;
cin >> n >> t;
for (int i = 1; i <= n; i++) cin >> a[i], a[i] += i - 1;
for (int i = 1; i <= n; i++) cin >> b[i];
int ans = -1, p = -1;
for (int i = 1; i <= n; i++) {
if (a[i] <= t && b[i] > ans) {
ans = b[i];
p = i;
}
}
cout << p << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int T = 1;
cin >> T;
while (T--) {
solve();
}
return 0;
}
B. Karina and Array(排序,贪心)
题意:给你一个数组有正有负,要求取出两个数使得乘积最大。
思路:如果全正,取最大两个。如果全负,取最小两个。如果有正有负如果n=2,可以称为是最大两个或者最小两个。如果n > 3,我们肯定取同号的,负数取两个最小的,正数取两个最大的,所以我们只需排序之后,取最大或者最小两个。
#include <bits/stdc++.h>
#define ll long long
#define ls (id << 1)
#define rs (id << 1 | 1)
using namespace std;
const int N = 2e5 + 5;
int a[N];
void solve() {
int n;
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
sort(a + 1, a + 1 + n);
cout << max(1ll * a[1] * a[2], 1ll * a[n] * a[n - 1]) << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int T = 1;
cin >> T;
while (T--) {
solve();
}
return 0;
}
C. Bun Lover(观察,找规律)
题意:给你一个圈圈饼,要求求出巧克力边的总长度。
思路:观察发现,边长大致符合1 + 2 + … + n 我们可以发现答案。
#include <bits/stdc++.h>
#define ll long long
#define ls (id << 1)
#define rs (id << 1 | 1)
using namespace std;
const int N = 2e5 + 5;
void solve() {
ll n;
cin >> n;
cout << n * (n + 1) + n + 2 << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int T = 1;
cin >> T;
while (T--) {
solve();
}
return 0;
}
D. Super-Permutation(数学、找规律)
题意:找到一个排列a使得,bi = (a1 + a2 + … + ai) % n + 1 也是一个排列。
思路:首先考虑什么时候是无解的,当n>1时,且为奇数的时候, bn = n * (n + 1) / 2 % n + 1 = 1,首先如果说ak=n, k必须是在1处的,因为如果 k > 1的话, bk = (bk-1 + ak) = bk-1不符合排列的定义。所以a1 = n, b1 = 1 = bn矛盾。观察发现,样例的余数是 0 5 1 4 2 3。我们模仿构造,观察发现,大余数是依次 5 4 3,前一个r > 上一个mod 可以直接作差得到。小余数的话,我们发现和上一个余数恰好互补,
#include <bits/stdc++.h>
#define ll long long
#define ls (id << 1)
#define rs (id << 1 | 1)
using namespace std;
const int N = 2e5 + 5;
int a[N];
void solve() {
int n;
cin >> n;
if (n == 1) {
cout << 1 << '\n';
return;
}
if (n & 1) {
cout << -1 << '\n';
return;
}
a[1] = n; //0 n - 1 1
int mod = 0, r = n;//0 5 1 4 2 3
for (int i = 2; i <= n; i++) {
if (i % 2 == 0) r--, a[i] = r - mod;
else a[i] = n + 1 - a[i - 1];
mod = (mod + a[i]) % n;
}
for (int i = 1; i <= n; i++) cout << a[i] << " \n"[i == n];
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int T = 1;
cin >> T;
while (T--) {
solve();
}
return 0;
}
E. Making Anti-Palindromes(字符串,贪心)
题意:通过交换字母,使得字符串对称位置均不相同,求出最小操作次数。
思路:首先考虑无解的情况,有两种,一种奇数的情况,中间处必然对称相等,另一种是某种字符数超过一半的情况。其余必定有解。那么如何最少操作,首先我们统计每种字符的对称相同的情况。如果说我们有两个不同的对称相等的字符对,只需交换一次,便可以减少两个相同对,这是最好的情况,一换二。但是,如果说某种字符对的数量过多,那么必须和其他本来就两侧不同的字符对单独交换,一换一。
#include <bits/stdc++.h>
#define ll long long
#define ls (id << 1)
#define rs (id << 1 | 1)
using namespace std;
const int N = 2e5 + 5;
int cnt[26], cp[26];
void solve() {
int n;
cin >> n;
for (int i = 0; i < 26; i++) cnt[i] = cp[i] = 0;
string s;
cin >> s;
if (n & 1) {
cout << -1 << '\n';
return;
}
for (auto x : s) {
cnt[x - 'a']++;
}
for (int i = 0; i < 26; i++) {
if (cnt[i] > n - cnt[i]) {
cout << -1 << '\n';
return;
}
}
int l = 0, r = n - 1, all = 0;
while (l < r) {
if (s[l] == s[r]) cp[s[l] - 'a']++, all++;
l++, r--;
}
int ans = 0, over = 0;
for (int i = 0; i < 26; i++) {
if (cp[i] > all - cp[i]) over += 2 * cp[i] - all;
}
cout << (all - over + 1) / 2 + over << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int T = 1;
cin >> T;
while (T--) {
solve();
}
return 0;
}
F. Gardening Friends(树的直径)
引理:一个点的最远点必然是树的直径的一端。
证明:(图略)假设直径两端为s,t。对于某个点x,其最远点为y,为直径外一点(在直径上很容易证明)。1、如果说x->y与s->t没有交点,必定有一个路径s->x(树的联通性)那么|x->y| > |x->s->t| 与树的直径矛盾。2、如果x->y与s->t有交点,记作o,|s->o| < |s->x->o|, |t->o| < |t->y->o|,|s->t| < |s->x->y->t|,矛盾。
题意:(可恶啊我读错成所有距离之和了)定义value为根到某个最深的点的 * k, 移动根一个单位花费c,求出最大的剩余价值。
思路:先找到直径一端,搜出该点为根的各点深度,再得到另一端,搜出各点深度,两者取大便是最远点的深度。
碎碎念:指针的使用可以让dfs复用,可以减少代码量。
#include <bits/stdc++.h>
#define ll long long
#define ls (id << 1)
#define rs (id << 1 | 1)
using namespace std;
const int N = 2e5 + 5;
vector<int> g[N];
int d[N], d1[N], d2[N];
void dfs(int u, int fa, int *d) {
d[u] = d[fa] + 1;
for (auto v : g[u]) {
if (v == fa) continue;
dfs(v, u, d);
}
}
void solve() {
int n, k, c;
cin >> n >> k >> c;
for (int i = 1; i <= n; i++) g[i].clear();
for (int i = 1; i < n; i++) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1, 0, d);
int maxn = 0, s;
for (int i = 1; i <= n; i++) {
if (d[i] > maxn) maxn = d[i], s = i;
}
dfs(s, 0, d1);
maxn = 0;
for (int i = 1; i <= n; i++) {
if (d1[i] > maxn) maxn = d1[i], s = i;
}
dfs(s, 0, d2);
ll ans = -1e18;
for (int i = 1; i <= n; i++) {
ans = max(ans, 1ll * max(d1[i] - 1, d2[i] - 1) * k - 1ll * (d[i] - 1) * c);
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int T = 1;
cin >> T;
while (T--) {
solve();
}
return 0;
}
G1. Magic Triples (Easy Version)[暴力、数学]
题意:给出2e5个数,数的范围是1~1e6。
思路:考虑根号分治,枚举平方因子,枚举每一个ak,时间复杂度为O(nsqrt(a)+K),K是复杂度包含map的log,1e6的因子个数240左右,估计可得总运算次数不超过4e8时限4s绰绰有余。
#include <bits/stdc++.h>
#define ll long long
#define ls (id << 1)
#define rs (id << 1 | 1)
using namespace std;
const int N = 2e5 + 5;
int a[N];
void solve() {
map<int, int> mp;
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
mp[a[i]]++;
}
ll ans = 0;
for (int i = 1; i <= n; i++) {
int cnt = mp[a[i]];
if (cnt >= 3) ans += 1ll * (cnt - 1) * (cnt - 2);
for (int j = 2; j * j <= a[i]; j++) {
if (a[i] % (j * j) == 0) {
ans += 1ll * mp[a[i] / j] * mp[a[i] / j / j];
}
}
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int T = 1;
cin >> T;
while (T--) {
solve();
}
return 0;
}
G2 - Magic Triples (Hard Version) [值域分治]
题意:a的范围为1e9,其余相同
思路:考虑值域分治,这里我们枚举的是等比数列的中间项aj,当aj <= 1e6的时候,我们可以直接枚举因子b,根号分治;当a>1e6的时候,b一定不超过1000,直接枚举b即可。时间复杂度为O(na^(0.333)logn + K) 这题容易卡常,用unmap由于种子已知会被人构造恰好次次哈希的冲突的数据(哈希表内部有链表的结构,发现哈希冲突的时候会调用链表),从而unmap退化为n2复杂度,不可接受,可以使用手写哈希,手写哈希也不一定快多少,因为自己造的种子,还是容易发生哈希冲突,常数也不小。这里记忆了数组,因为考虑到这里是捆绑测试,可能出现强的数据点*t的情况(test55),注意这里必须要用count判断是否存在,因为如果不存在会开辟新的空间,从而增加map的深度,大幅度提高常数(tle4)。
#include <bits/stdc++.h>
#define ll long long
#define ls (id << 1)
#define rs (id << 1 | 1)
using namespace std;
const int N = 2e5 + 5;
ll a[N];
map<vector<int>, int> trick;
void solve() {
map<ll, int> mp;
int n;
cin >> n;
vector<int> tag;
for (int i = 1; i <= n; i++) cin >> a[i], mp[a[i]]++, tag.push_back(a[i]);
ll ans = 0;
if (trick.count(tag)) {
cout << trick[tag] << '\n';
return;
}
for (int i = 1; i <= n; i++) {
int cnt = mp[a[i]];
if (cnt >= 3) ans += 1ll * (cnt - 1) * (cnt - 2);
}
for (int i = 1; i <= n; i++) {
if (a[i] <= 1000000) {
for (int j = 2; j * j <= a[i]; j++) {
if (a[i] % j == 0) {
if (mp.count(a[i] / j) && mp.count(a[i] * j)) ans += 1ll * mp[a[i] / j] * mp[a[i] * j];
if (a[i] != 1ll * j * j && mp.count(j) && mp.count(a[i] * (a[i] / j))) {
ans += mp[j] * mp[a[i] * (a[i] / j)];
}
}
}
if (a[i] > 1 && mp.count(1) && mp.count(a[i] * a[i])) {
ans += mp[1] * mp[a[i] * a[i]];
}
} else {
for (int b = 2; b <= 1000; b++) {
if (a[i] % b == 0 && mp.count(a[i] / b) && mp.count(a[i] * b)) {
ans += 1ll * mp[a[i] / b] * mp[a[i] * b];
}
}
}
}
cout << ans << '\n';
trick[tag] = ans;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int T = 1;
cin >> T;
while (T--) {
solve();
}
return 0;
}