27th CCF CSP | 第27次专业级软件能力认证 游记&题解

游记

前言

第一次参加专业级 CSP,上大学了还要继续与 CCF 打交道。

试卷 pdf 排版和 CSP-J/S,NOIP 一模一样,有过去的味道了。


题解

Score

100 + 100 + 100 + 30 + 70 = 400

提前 40 分钟交卷出场。

排名:39

A 如此编码

概述

得分 100,1A

通过时间:+5 min

题解

一开始不会,直到看到提示。

提示给出如下公式:

\[m \bmod c_{i} = \sum\limits_{j=1}^{i-1} c_{j} \times b_{j+1} \]

作差,除去 \(c_j\) 即可。

Codes

#include<bits/stdc++.h>
using namespace std;

template <typename Tp>
void read(Tp &x) {
	x = 0; int fh = 1; char ch = 1;
	while(ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
	if(ch == '-') fh = -1, ch = getchar();
	while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
	x *= fh;
}

const int maxn = 20 + 7;

int n, m;
int a[maxn], c[maxn], b[maxn];

void Init(void) {
	read(n); read(m); c[0] = 1;
	for(int i = 1; i <= n; i++) {
		read(a[i]);
		c[i] = c[i - 1] * a[i];
	}
}

void Work(void) {
	for(int i = 1; i <= n; i++) {
		int tmp = m % c[i] - m % c[i - 1];
		b[i] = tmp / c[i - 1];
	}
	for(int i = 1; i <= n; i++) {
		printf("%d%c", b[i], " \n"[i == n]);
	}
}

signed main(void) {
	Init();
	Work();
//	system("pause");
	return 0;
}

B 何以包邮?

概述

得分:100,1A

通过时间:+11 min

简要题意

给出 \(\{ a_n \}\),选出一个子序列 \(\{ b_n \}\),使得 \(\sum b_i \ge M\),求 \(\min \sum b_i\)

数据范围 \(n \le 30, a_i \le 10^4\)

题解

\(S = \sum a_i\)

显而易见可以用 01 背包求出 \(0 - S\) 每一个容量的可达性,从 \(M\) 向后枚举直至得到答案即可。

时间复杂度 \(\mathcal O( \sum a_i )\)

Codes

#include<bits/stdc++.h>
using namespace std;

template <typename Tp>
void read(Tp &x) {
	x = 0; int fh = 1; char ch = 1;
	while(ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
	if(ch == '-') fh = -1, ch = getchar();
	while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
	x *= fh;
}

const int maxn = 30 + 7;

int n, lim, S;
int a[maxn];
bool dp[500000];

void Init(void) {
	read(n); read(lim);
	for(int i = 1; i <= n; i++) {
		read(a[i]); S += a[i];
	}
}

void Work(void) {
	dp[0] = true;
	for(int i = 1; i <= n; i++) {
		for(int j = S; j >= a[i]; j--) {
			dp[j] |= dp[j - a[i]];
		}
	}
	for(int i = lim; i <= S; i++) {
		if(dp[i]) {
			printf("%d\n", i); return ;
		}
	}
}

signed main(void) {
	Init();
	Work();
//	system("pause");
	return 0;
}

C 防疫大数据

概述

恶臭大模拟。

得分:100,1A

通过时间:+59 min

Codes

#include<bits/stdc++.h>
using namespace std;

template <typename Tp>
void read(Tp &x) {
	x = 0; int fh = 1; char ch = 1;
	while(ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
	if(ch == '-') fh = -1, ch = getchar();
	while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
	x *= fh;
}

const int maxn = 1000 + 7;

struct DayInfo {
	int r, m;
	int d[maxn], u[maxn], a[maxn];
	int rec[maxn];
}info[maxn];

//struct area_danger {
//	int date, id;
//};
//
//unordered_map <int, int> inq;
//queue <area_danger> danger;
//
//void RemoveDanger(int D) {
//	while(danger.size()) {
//		area_danger T = danger.front();
//		if(T.date <= D - 7) {
//			danger.pop(); inq[T.id]--;
//		}
//		else break;
//	}
//}
//
//void AddDanger(int date, int id) {
//	area_danger T; T.date = date, T.id = id;
//	danger.push(T); inq[id]++;
//}
//
//bool CheckDanger(int date, int id) {
//	if(inq[id] > 0) return true;
//	return false;
//}

//vector <int> dangerlist[maxn];

bool CheckDanger(int date, int id) {
	for(int i = max(0, date - 6); i <= date; i++) {
		for(int j = 1; j <= info[i].r; j++) {
			if(info[i].rec[j] == id) return true;
		}
	}
	return false;
}



int n, torec[maxn];

struct Active_User {
	int date, id;
};

unordered_map <int, int> user;
queue <Active_User> act; 

void RemoveActive(int D) {
	while(act.size()) {
		Active_User T = act.front();
		if(T.date <= D - 7) {
			act.pop(); user[T.id]--;
		}
		else break;
	}
}

void AddActive(int date, int id) {
	Active_User T; T.date = date, T.id = id;
	act.push(T); user[id]++;
}

void Init(void) {
	read(n);
	for(int nowday = 0; nowday < n; nowday++) {
//		RemoveDanger(nowday);
		read(info[nowday].r); read(info[nowday].m);
		for(int i = 1; i <= info[nowday].r; i++) {
			read(info[nowday].rec[i]);
//			read(torec[i]); AddDanger(nowday, torec[i]);
		}
		for(int i = 1; i <= info[nowday].m; i++) {
			read(info[nowday].d[i]); read(info[nowday].u[i]); read(info[nowday].a[i]);
		}
		//date user area
		vector <int> userlist;
		set <int> inv;
		for(int i = max(nowday - 6, 0); i <= nowday; i++) {
			for(int j = 1; j <= info[i].m; j++) {
				if(info[i].d[j] <= nowday - 7) continue;
				if(inv.find(info[i].u[j]) != inv.end()) continue;
				bool ok = true;
				for(int k = info[i].d[j]; k <= nowday; k++) {
					if(CheckDanger(k, info[i].a[j]) == false) {
						ok = false; break;
					}
				}
				if(ok) {
					userlist.push_back(info[i].u[j]);
					inv.insert(info[i].u[j]);
				}
			}
		}
		sort(userlist.begin(), userlist.end());
		printf("%d ", nowday);
		for(auto U : userlist) {
			printf("%d ", U);
		}
		puts("");
	}
}

void Work(void) {

}

signed main(void) {
	Init();
	Work();
//	system("pause");
	return 0;
}

D 吉祥物设置

概述

得分:30

通过 \(n \le 10000,m=1\) 共两档部分分。

正解

对于每一种颜色,共建立 \(m\) 棵线段树。

E 高维亚空间超频物质变压缩技术

概述

得分:70

通过 Subtask 1, 2, 3

Subtask 1, 2

\(n \le 2000\)

经典 dp,设 \(f(i)\) 代表以 \(1-i\) 合并为若干段的最小代价。

\[f(i) = \min \{ f(j) + [S(i) - S(j) - L]^2 \}, \text{while } m_j < m_i \]

其中 \(S(n) = \sum\limits_{i=1}^n{v_i}\)

时间复杂度为 \(\mathcal O(n^2)\)

Subtask 3

\(n \le 10^5\),保证 \(m\) 递增。

去掉了对 \(m_j\) 的限制,上述式子是个典型的斜率优化。

去掉 \(\min\),假设转移点为 \(j\)\(f(i) = f(j) + S(i)^2 - 2LS(i) - 2S(i)S(j) + S(j)^2 + 2LS(j) + L^2\)

移项后,有

\[f(j) + S(j)^2 + 2LS(j) = 2S(i)S(j) + f(i) - S(i)^2 + 2LS(i) - L^2 \]

对于一个直线 \(y = kx+b\),即有 \(y = f(j)+S(j)^2+2LS(j),k=2S(i),x=S(j),b=f(i)-S(i)^2+2LS(i)-L^2\)

由于 \(S(i)\) 严格单调增,即有 \(k,x\) 严格单调增,用优先队列维护决策点即可。

posted @ 2022-09-18 21:14  览遍千秋  阅读(1973)  评论(3编辑  收藏  举报