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
题解
一开始不会,直到看到提示。
提示给出如下公式:
作差,除去 \(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\) 合并为若干段的最小代价。
其中 \(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\)
移项后,有
对于一个直线 \(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\) 严格单调增,用优先队列维护决策点即可。