A

算法标签: 模拟

在这里插入图片描述

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	string s1, s2;
	cin >> s1 >> s2;

	if (s1 == "sick" && s2 == "fine") cout << 2 << "\n";
	else if (s1 == "fine" && s2 == "sick") cout << 3 << "\n";
	else if (s1 == "fine" && s2 == "fine") cout << 4 << "\n";
	else cout << 1 << "\n";

	return 0;
}

B

算法标签: 模拟

在这里插入图片描述

思路

每次找到 B B B的位置, 以 B B B为中心向两侧扩展, 然后删除该位置

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	string s;
	cin >> s;

	int res = 0;
	while (s.find('B') != -1) {
		int u = s.find('B');
		
		int n = s.size();
		for (int i = 1; i <= n; ++i) {
			if (u - i < 0 || u + i >= n) continue;
			if (s[u - i] == 'A' && s[u + i] == 'C') res++;
		}

		s[u] = ' ';
	}

	cout << res << "\n";

	return 0;
}

C

算法标签: 哈希映射, 哈希表

在这里插入图片描述

思路

注意计算哈希值的时候转化成 l o n g   l o n g long \, long longlong

#include <iostream>
#include <algorithm>
#include <cstring>
#include <unordered_set>
#include <utility>

using namespace std;

typedef long long LL;
const int N = 2e5 + 10, M = 5e5 + 10;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	int n, m;
	cin >> n >> m;

	unordered_set<LL> s;
	int res = 0;

	while (m--) {
		int u, v;
		cin >> u >> v;

		if (u == v) {
			res++;
			continue;
		}

		LL hash1 = (LL)u * N + v;
		LL hash2 = (LL)v * N + u;
		if (!s.count(hash1) && !s.count(hash2)) {
			s.insert(hash1);
			s.insert(hash2);
		}
		else res++;
	}

	cout << res << "\n";

	return 0;
}

D

算法标签: 贪心, 模拟

在这里插入图片描述

思路

因为最后一定是一段 1 1 1连接到一起, 因此设 1 1 1的数量是 c n t cnt cnt, 最后 1 1 1的起始位置 l l l, 那么终点坐标就是 l + c n t − 1 l + cnt - 1 l+cnt1, 设初始时每个 1 1 1的位置是 a i a_i ai, 那么目标就是最小化

a n s = min ⁡ { ∑ i = 1 n ∣ a i − ( l + i − 1 ) ∣ } ans =\min\left \{ \sum _{i = 1} ^ {n} |a_i - (l + i - 1)| \right \} ans=min{i=1nai(l+i1)}

整理后得到

a n s = min ⁡ { ∑ i = 1 n ∣ a i − i − ( l − 1 ) ∣ } ans =\min\left \{ \sum _{i = 1} ^ {n} |a_i - i - (l - 1)| \right \} ans=min{i=1naii(l1)}

b i = a i − i b_i = a_i - i bi=aii, x = l − 1 x = l - 1 x=l1, 那么原式变为

a n s = min ⁡ { ∑ i = 1 n ∣ b i − x ∣ } ans =\min\left \{ \sum _{i = 1} ^ {n} |b_i - x| \right \} ans=min{i=1nbix}

x x x取中位数就是最小值

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>

using namespace std;

typedef long long LL;
const int N = 5e5 + 10;

int n;
string s;
vector<int> nums;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	cin >> n >> s;
	int k = 1;
	for (int i = 0; i < n; ++i) {
		if (s[i] == '1') {
			nums.push_back(i - k);
			k++;
		}
	}
	
	sort(nums.begin(), nums.end());
	int x = nums[nums.size() / 2];
	
	LL res = 0;
	for (int val : nums) res += abs(val - x);
	
	cout << res << "\n";
	return 0;
}

E

算法标签: 倍数, 数论, g c d gcd gcd, 调和级数

在这里插入图片描述

思路

首先看 A i A_i Ai的最大值范围不超过 1 0 6 10 ^ 6 106, 也就是答案 g c d ≤ 1 0 6 gcd \le 10 ^ 6 gcd106, 因此可以枚举 g c d gcd gcd, 题目要求 k k k个元素中必须包含 A i A_i Ai, 设 d = g c d d = gcd d=gcd, 必须满足 d ∣ A i d | A_i dAi, 并且在剩余元素中至少有 k − 1 k - 1 k1 d d d的倍数

c n t [ x ] cnt[x] cnt[x]表示 A A A x x x出现的次数, m u l [ x ] mul[x] mul[x]表示 A A A x x x倍数出现的次数, 那么有 m u l [ x ] = c n t [ x ] + c n t [ 2 x ] + c n t [ 3 x ] + . . . + c n t [ n x ] mul[x] = cnt[x] + cnt[2x] + cnt[3x] + ... + cnt[nx] mul[x]=cnt[x]+cnt[2x]+cnt[3x]+...+cnt[nx], 调和级数枚举每个 x x x的倍数, 时间复杂度 O ( n ln ⁡ n ) O(n \ln n) O(nlnn), 可以放缩为 O ( n log ⁡ n ) O(n \log n) O(nlogn)

算法过程

  • 首先枚举 d d d
  • 然后检查 m u l [ d ] ≥ k mul[d] \ge k mul[d]k, 如果满足条件, 更新 d d d的倍数的 A i A_i Ai的答案

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 1.2E6 + 10, M = 1e6 + 10;

int n, k;
int w[N], cnt[N], mul[N];
int res[N];

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	cin >> n >> k;
	for (int i = 1; i <= n; ++i) {
		cin >> w[i];
		cnt[w[i]]++;
	}

	// 调和级数枚举所有数倍数出现的次数
	for (int i = 1; i < M; ++i) {
		for (int j = i; j < M; j += i) {
			mul[i] += cnt[j];
		}
	}

	for (int d = 1; d < M; ++d) {
		if (mul[d] < k) continue;
		for (int x = d; x < N; x += d) {
			res[x] = max(res[x], d);
		}
	}

	for (int i = 1; i <= n; ++i) cout << res[w[i]] << "\n";
	return 0;
}

F

算法标签: 离线, 树状数组, 离散化, 动态规划, L I S LIS LIS

在这里插入图片描述

思路

注意到, 查询数量和序列长度都是 2 × 1 0 5 2 \times 10 ^ 5 2×105, O ( n 2 ) O(n ^ 2) O(n2)的直接做法会超时
L I S LIS LIS问题有一个树状数组优化做法, 设 f [ j ] f[j] f[j]表示以 j j j结尾的最长上升子序列的长度, 更新方式 f [ a [ j ] ] = max ⁡ k < a [ i ] { f [ k ] } + 1 f[a[j]] = \max _{k < a[i]} \left \{ f[k] \right \} + 1 f[a[j]]=maxk<a[i]{f[k]}+1, 取 m a x max max的过程可以使用树状数组优化

树状数组求最长上升子序列问题
在这里插入图片描述

树状数组求 L I S LIS LIS

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>

using namespace std;

const int N = 1010;

int n, w[N];
vector<int> nums;
int tr[N];

int get(int val) {
	return lower_bound(nums.begin(), nums.end(), val) - nums.begin() + 1;
}

void disc() {
	for (int i = 1; i <= n; ++i) nums.push_back(w[i]);
	sort(nums.begin(), nums.end());

	for (int i = 1; i <= n; ++i) w[i] = get(w[i]);
}

int lowbit(int val) {
	return val & -val;
}

void update(int u, int val) {
	for (int i = u; i < N; i += lowbit(i)) tr[i] = max(tr[i], val);
}

int get_max(int u) {
	int res = 0;
	for (int i = u; i; i -= lowbit(i)) res = max(res, tr[i]);
	return res;
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	cin >> n;
	for (int i = 1; i <= n; ++i) cin >> w[i];

	disc();

	int res = 1;
	for (int i = 1; i <= n; ++i) {
		int curr = get_max(w[i] - 1) + 1;
		res = max(res, curr);
		update(w[i], curr);
	}

	cout << res << "\n";

	return 0;
}

因为当前问题是离线问题, 因此可以将查询顺序打乱, 考虑将查询按照 R R R从小到大排序依次处理, 每次处理完 a [ i ] a[i] a[i]后计算 R = i R = i R=i的所有询问, 时间复杂度 O ( ( N + Q ) l o g M ) O((N + Q) log M) O((N+Q)logM), M M M是树状数组大小

树状数组做法

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>

using namespace std;

const int N = 2e5 + 10;

int n, q;
int w[N];
struct Query {
    int x, id;
};
vector<Query> qs[N];
int tr[N << 1], ans[N];

// 离散化
void disc() {
    vector<int> nums;
    for (int i = 1; i <= n; ++i) nums.push_back(w[i]);

    for (int i = 1; i <= n; ++i) {
        for (auto [x, id] : qs[i]) {
            nums.push_back(x);
        }
    }

    sort(nums.begin(), nums.end());
    nums.erase(unique(nums.begin(), nums.end()), nums.end());

    for (int i = 1; i <= n; ++i) {
        w[i] = lower_bound(nums.begin(), nums.end(), w[i]) - nums.begin() + 1;
    }

    for (int i = 1; i <= n; ++i) {
        for (Query &q : qs[i]) {
            q.x = lower_bound(nums.begin(), nums.end(), q.x) - nums.begin() + 1;
        }
    }
}

int lowbit(int val) {
    return val & -val;
}

void update(int u, int val) {
    for (int i = u; i < N * 2; i += lowbit(i)) {
        tr[i] = max(tr[i], val);
    }
}

int get_max(int u) {
    int res = 0;
    for (int i = u; i > 0; i -= lowbit(i)) {
        res = max(res, tr[i]);
    }
    return res;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    cin >> n >> q;
    for (int i = 1; i <= n; ++i) cin >> w[i];

    for (int i = 1; i <= q; ++i) {
        int r, x;
        cin >> r >> x;
        // 记录最大值范围和查询id
        qs[r].push_back({x, i});
    }

    disc();

    for (int i = 1; i <= n; ++i) {
        int curr = get_max(w[i] - 1) + 1;
        update(w[i], curr);
        for (Query q : qs[i]) {
            ans[q.id] = get_max(q.x);
        }
    }

    for (int i = 1; i <= q; ++i) cout << ans[i] << "\n";
    return 0;
}

动态规划 + 二分做法

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>

using namespace std;

const int N = 2e5 + 10;

int n, q, w[N];
struct Query {
	int r, x, id;
	int ans;
} qs[N];

bool cmp_r(Query &q1, Query &q2) {
	return q1.r < q2.r;
}

bool cmp_id(Query &q1, Query &q2) {
	return q1.id < q2.id;
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	cin >> n >> q;
	for (int i = 1; i <= n; ++i) cin >> w[i];

	for (int i = 1; i <= q; ++i) {
		int r, x;
		cin >> r >> x;
		qs[i] = {r, x, i};
	}

	sort(qs + 1, qs + q + 1, cmp_r);

	vector<int> f;
	f.push_back(0);

	int qi = 1;
	for (int i = 1; i <= n; ++i) {
		// 第一个大于等于w[i]的位置
		auto it = lower_bound(f.begin(), f.end(), w[i]);
		if (it == f.end()) f.push_back(w[i]);
		else *it = w[i];

		while (qi <= q && qs[qi].r == i) {
			qs[qi].ans = upper_bound(f.begin(), f.end(), qs[qi].x) - f.begin() - 1;
			qi++;
		}
	}

	sort(qs + 1, qs + q + 1, cmp_id);
	for (int i = 1; i <= q; ++i) cout << qs[i].ans << "\n";

	return 0;
}


G

先鸽, 因为是黑题, 有时间补上

Logo

集算法之大成!助力oier实现梦想!

更多推荐