ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

The 17th Zhejiang Provincial Collegiate Programming Contest

2022-02-28 11:02:23  阅读:194  来源: 互联网

标签:Provincial 17th Contest int LL tr return 31 dp


比赛链接:

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]\)。
问题就变成了找 \([l, r]\) 这个区间中最大的 \(m\) 个数,可以通过主席树实现。

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 1e5 + 10;
LL T, n, q, idx, root[N], a[N], b[N], l, r, k;
struct node{
	LL l, r, cnt, sum;
}tr[N * 20];
LL update(LL p, LL l, LL r, LL k) {
    LL x = ++ idx;
    tr[x].l = tr[p].l;
    tr[x].r = tr[p].r;
    tr[x].cnt = tr[p].cnt + 1;
    tr[x].sum = tr[p].sum + b[k];
    if (l < r) {
        LL mid = l + r >> 1;
        if(k <= mid) tr[x].l = update(tr[p].l, l, mid, k);
        else tr[x].r = update(tr[p].r, mid + 1, r, k);
    }
    return x;
}
LL query(LL p,LL q,LL k,LL l,LL r) {
    if(l == r) return b[l] * k;
    LL x = tr[tr[q].r].cnt - tr[tr[p].r].cnt, mid = l + r >> 1;
    if (k <= x) return query(tr[p].r, tr[q].r, k, mid + 1, r);
    return tr[tr[q].r].sum - tr[tr[p].r].sum + query(tr[p].l, tr[q].l, k - x, l, mid);
}
LL c(LL x) {
    return x * (x + 1) / 2 * (2 * x + 1) / 3;
}
void solve(){
	idx = 0;
    cin >> n;
    for(int i = 1; i <= n; ++ i) {
        scanf("%lld", &a[i]);
        b[i] = a[i];
    }
    sort(b + 1, b + 1 + n);
    LL len = unique(b + 1, b + 1 + n) - b - 1;
    for(int i = 1; i <= n; ++ i) {
        a[i] = lower_bound(b + 1, b + len + 1, a[i]) - b;
        root[i] = update(root[i - 1], 1, len, a[i]);
    }
    scanf("%lld", &q);
    while(q--) {
        scanf("%lld%lld%lld", &l, &r, &k);
        cout << c(r - l + 1) + query(root[l - 1], root[r], k, 1, len) << "\n";
    }
}
int main() {
    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;
}

题目大意:

思路:

代码:

标签:Provincial,17th,Contest,int,LL,tr,return,31,dp
来源: https://www.cnblogs.com/Hamine/p/15940563.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有