比赛链接:

https://codeforces.com/gym/102770

A. AD 2020

题目大意:

输入两个日期(日期范围是从 20000101 到 99991231),判断这两个日期及它们中间有多少个日期包含 202 这个子串。

思路:

预处理出每一个日期是否包含 202,然后做一个前缀和,询问的时候直接输出就行。

代码:

#include <bits/stdc++.h>
using namespace std;
int T, a, b, c, d, e, f, t[10000][13][32], s[10005 * 14 * 35];
int cal(int x){
	while (x > 100){
		if (x % 1000 == 202) return 1;
		x /= 10;
	}
	return 0;
}
void init(){
	int day[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31}, cnt = 0;
	for (int y = 2000; y <= 9999; y++)
		for (int m = 1; m <= 12; m++){
			int k = day[m];
			if (m == 2 && ((y % 4 == 0 && y % 100 != 0) || (y % 400 == 0))) k++;
			for (int d = 1; d <= k; d++){
				t[y][m][d] = ++cnt;
				s[cnt] = s[cnt - 1] + cal((y * 100 + m) * 100 + d);
			}
		}
}
void solve(){
	cin >> a >> b >> c >> d >> e >> f;
	cout << s[t[d][e][f]] - s[t[a][b][c] - 1] << "\n";
}
int main(){
	init();
	cin >> T;
	while (T--)
		solve();
	return 0;
}

B. Bin Packing Problem

题目大意:

\(n\) 个物品,第 \(i\) 个物品为 \(a[i]\),有无限个容量为 \(c\) 的空箱子,现有两种装箱的方法。
第一种:选择最前面的能放下物品的箱子放入物品
第二种:选择能放下物品且剩余容量最小的箱子放物品
问两种装箱方法各需要多少个箱子才能装完所有物品。

思路:

第一种装箱方法我们可以通过线段树来做,每个节点的值就是该箱子的容量,通过查询前面的箱子有没有足够的容量去存放物品,如果有就向左递归,没有就向右。
第二种装箱方法可以用 multiset 来做,因为 multiset 是有序的,可以用 lower_bound() 二分查找,直接将物品放入能放下该物品的最小容量的那个箱子。

代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int T, n, a[N], c, tr[N << 2];
void pushup(int u){
	tr[u] = max(tr[u << 1], tr[u << 1 | 1]);
}
void build(int u, int l, int r){
	if (l == r) tr[u] = c;
	else {
		int mid = l + r >> 1;
		build(u << 1, l, mid);
		build(u << 1 | 1, mid + 1, r);
		pushup(u);
	}
}
void update(int u, int l, int r, int p, int k){
	if (l > p || r < p) return;
	if (l == r) tr[u] -= k;
	else {
		int mid = l + r >> 1;
		update(u << 1, l, mid, p, k);
		update(u << 1 | 1, mid + 1, r, p, k);
		pushup(u);
	}
}
int query(int u, int l, int r, int k){
	if (l == r){
		if (tr[u] >= k) return l;
		return n + 1;
	}
	int mid = l + r >> 1;
	if (tr[u << 1] >= k) return query(u << 1, l, mid, k);
	else return query(u << 1 | 1, mid + 1, r, k);
}
void ff(){
	build(1, 1, n);
	for (int i = 1; i <= n; i++)
		update(1, 1, n, query(1, 1, n, a[i]), a[i]);
	cout << query(1, 1, n, c) - 1 << " ";
}
void bf(){
	multiset <int> s;
	for (int i = 1; i <= n; i++){
		auto it = s.lower_bound(a[i]);
		if (it == s.end()) s.insert(c - a[i]);
		else {
			int x = *it;
			s.erase(it);  //multiset 可以存放重复数据,如果是删除某个值的话,会去掉多个箱子,导致答案错误,所以直接删除对应位置的元素
			s.insert(x - a[i]);
		}
	}
	cout << s.size() << "\n";
}
void solve(){
	cin >> n >> c;
	for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
	ff();
	bf();
}
int main(){
	cin >> T;
	while (T--)
		solve();
}

E.Easy DP Problem

题目大意:

给了一个 dp 转移方程,以及一个序列,进行 q 次询问,要求输出询问的区间中,执行该 dp 方程的某个状态的答案。

思路:

举个栗子,当 m = 2,k = 2 时。
dp[2][2] = \(2^2\) + max(dp[1][2], dp[1][1] + b[2]) = \(2^2\) + max(\(1^2\) + max(dp[0][2], dp[0][1] + b[1]), \(1^2\) + max(dp[0][1], dp[0][0] + b[1]) + b[2]) = \(2^2\) + max(\(1^2\) + max(0, b[1]), \(1^2\) + max(0, b[1]) + b[2]) = \(2^2 + 1^2\) + b[2] + b[1]
通过一些例子,我们可以推得 \(dp[m][k] = \sum_{i = 1}^m i^2 + \sum_{i = m - k + 1}^m b[i]\)
\(\sum_{i = 1}^m i^2\) 可以通过公式实现,那么问题就变成了找 \([l, r]\) 这个区间中最大的 \(m\) 个数,这个通过主席树实现。

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 1e5 + 10;
LL a[N], b[N];
struct ChairTree{
	int idx = 0, root[N];
	struct node{
		int l, r;
		LL cnt, sum;
	}tr[N * 20];
	int insert(int u, int l, int r, int k){
		int v = ++ idx;
		tr[v] = tr[u];
		tr[v].cnt ++ ;
		tr[v].sum += b[k];
		if (l < r){
			int mid = l + r >> 1;
			if (k <= mid) tr[v].l = insert(tr[u].l, l, mid, k);
			else tr[v].r = insert(tr[u].r, mid + 1, r, k);
		}
		return v;
	}
	LL query(int u, int v, int l, int r, LL k){
		if (l == r) return b[l] * k;
		LL res = tr[tr[v].r].cnt - tr[tr[u].r].cnt;
		int mid = l + r >> 1;
		if (k <= res) return query(tr[u].r, tr[v].r, mid + 1, r, k);
		else return tr[tr[v].r].sum - tr[tr[u].r].sum + query(tr[u].l, tr[v].l, l, mid, k - res);
	}
}ct;
LL C(LL x){
	return x * (x + 1) / 2 * (2 * x + 1) / 3;
}
void solve(){
	ct.idx = 0;
	int n;
	cin >> n;
	for (int i = 1; i <= n; i ++ ){
		cin >> a[i];
		b[i] = a[i];
	}
	sort(b + 1, b + n + 1);
	int len = unique(b + 1, b + n + 1) - b - 1;
	for (int i = 1; i <= n; i ++ ){
		a[i] = lower_bound(b + 1, b + len + 1, a[i]) - b;
		ct.root[i] = ct.insert(ct.root[i - 1], 1, len, a[i]);
	}
	int q;
	cin >> q;
	while(q -- ){
		int l, r, k;
		cin >> l >> r >> k;
		cout << C(r - l + 1) + ct.query(ct.root[l - 1], ct.root[r], 1, len, k) << "\n";
	}
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	int T;
	cin >> T;
	while(T -- ){
		solve();
	}
	return 0;
}

K. Killing the Brute-force

题目大意:

给定标程和暴力方法在 \(n\) = 1,2,3... 所花的时间,找到最小的 \(n\) 使得三倍标程的时间可以卡掉暴力方法,即暴力方法的时间大于三倍标程时间。

思路:

就是判断 b 有没有大于 3 * a,简单地遍历一遍判断就行

代码:

#include <bits/stdc++.h>
using namespace std;
int T, n;
void solve(){
	cin >> n;
	vector <int> a(n), b(n);
	for (int i = 0; i < n; i++)
		cin >> a[i];
	for (int i = 0; i < n; i++)
		cin >> b[i];
	for (int i = 0; i < n; i++)
		if (a[i] * 3 < b[i]){
			cout << i + 1 << "\n";
			return;
		}
	cout << "-1\n";
}
int main(){
	cin >> T;
	while (T--)
		solve();
	return 0;
}

I. Invoking the Magic

题目大意:

\(n\) 双被弄混的袜子,即一双袜子中两只是不同的,每只袜子有且只有两只,现在有一个神奇的洗衣机,放入 \(n\) 双被弄混袜子后,它可以将它们对称地弄好,但是放入的每一种袜子都要有两只。洗衣机的大小由放入袜子的数量决定,输出洗衣机最小能是多少。

思路:

对于 \((a, b)\) 这一对袜子,我们得找到另一只 \(a\) 和另一只 \(b\),同样的,当我们找的这一堆袜子中任何一只没有匹配,我们都要一直找下去,知道所有袜子都能匹配。
本质就是求连通块中点的数量,输出最小的那一个连通块的大小。

代码:

#include <bits/stdc++.h>
using namespace std;
#define all(x) (x).begin(),(x).end()
#define fi first
#define pb push_back
#define pii pair <int, int>
#define se second
const int N = 1e6 + 10;
int T, n, a, b, c[N], cnt, ans;
vector <int> num, e[N];
vector <pii> p;
map <int, int> mp;
void init(){
	cnt = 0;
	ans = 0;
	num.clear();
	p.clear();
	mp.clear();
	for (int i = 1; i <= n; i++)
		e[i].clear(), c[i] = 0;
}
int find(int x){
	int l = 0, r = num.size() - 1;
	while (l < r){
		int mid = l + r >> 1;
		if (num[mid] >= x) r = mid;
		else l = mid + 1;
	}
	return r + 1;
}
void dfs(int x){
	for (auto i : e[x]){
		if (c[i]) continue;
		c[i] = cnt;
		dfs(i);
	}
}
void solve(){
	cin >> n;
	init();
	for (int i = 1; i <= n; ++ i){
		scanf("%d%d", &a, &b);
		p.pb({a, b});
		num.pb(a);
		num.pb(b);
	}
	sort(all(num));
	num.erase(unique(all(num)), num.end());
	for (auto i : p){
		int x = find(i.fi), y = find(i.se);
		e[x].pb(y);
		e[y].pb(x);
	}
	for (int i = 1; i <= n; ++ i){
		if (!c[i]){
			cnt++;
			dfs(i);
		}
		mp[c[i]]++;
	}
	for (auto i : mp)
		ans = max(ans, i.se);
	cout << ans << "\n";
}
int main(){
	cin >> T;
	while (T--)
		solve();
	return 0;
}
posted on 2022-02-28 10:59  Hamine  阅读(165)  评论(0编辑  收藏  举报