本次报告的撰写人:WilliamZH
题目:202109,第23次CCFCSP
使用语言:C++
题目 1:数组推导
题目描述:
给定一个由n个自然数组成的数组B,其中Bi等于数组A中前i个数的最大值。
求在所有可能的A中,A的总和sum的最大值和最小值。
$𝑛 ≤ 100, 0 ≤ 𝐵₁ ≤ 𝐵₂ ≤… ≤𝐵_n ≤ 10⁵$。
题解:
分为sum最大值和sum最小值两部分。
sum最大值:
对于每一个$A_i$,一定有$A_i≤B_i$,则sum的最大值, 即$B_i$的和。
sum最小值:
解法一:
因为数组B单调递增,若$B_i=B_{i-1}$,则说明0≤$A_i$≤$B_i$;若$B_i$>$B_{i-1}$,则说明$Ai=Bi$。
上述两种情况都取最小值即为所求。
时间复杂度$𝑂(𝑛)$,空间复杂度$𝑂(𝑛)$,期望得分100。
1 |
|
解法二:
对空间复杂度的改进。
无需用数组存储读入的数据,读入数据流中的数据进行计算处理即可。
时间复杂度$𝑂(𝑛)$,空间复杂度$𝑂(1)$,期望得分100。
1 |
|
题目 2:非零段划分
题目描述:
给定一个长度为 𝑛 的非负整数数组 $𝑎₁, 𝑎₂, ⋯ , 𝑎_𝑛$,要求将小于正整数 x 的数全部变为0,求出非零段段数的最大值。
𝑛 ≤ 5 × 10⁵ , 0 ≤ $𝑎_𝑖$ ≤ 10⁴。
题解:
设 $V$ 为数组中不同元素个数。
解法一:
𝑛 ≤ 1000。
利用set的自动去重功能,统计不同元素的个数。枚举set中的每个元素作为选取的值$x$,遍历数组$A$,如果 $𝑎i⩾x$ 且 $𝑎_{i-1}<x$,则计数器+1。根据所有选取的值$x$,计算出非零段个数的最大值。
时间复杂度$𝑂(V𝑛log𝑛)$,空间复杂度$𝑂(𝑛)$。可通过70%的数据。
1 |
|
解法二:
$vector$ 数组$v[i]$存储数字 $i$ 的位置
从最大的数开始逆向遍历,(默认vs bool数组为0,某个位置数不为0,则对应vs 为1)
- 若一个数比旁边两个数都大,那p就+1;
- 若一个大一个小就不变;
- 若比两个都小就-1。
时间复杂度:$𝑂(𝑛log𝑛)$,空间复杂度:$O(n)$,期望得分:100。
1 |
|
题目 3:脉冲神经网络
题目描述:
有一个图,每个节点分为脉冲源和神经元。每个节点有权值 (𝑢, 𝑣),计算方式为:
𝑣𝑘 = 𝑣𝑘−1 + 𝛥𝑡(0.04𝑣𝑘 2 −1 + 5𝑣𝑘−1 + 140 − 𝑢𝑘 − 1) + 𝐼𝑘
𝑢𝑘 = 𝑢𝑘−1 + 𝛥𝑡(𝑏𝑣𝑘−1 − 𝑢𝑘−1 )
脉冲源每秒有一个脉冲,按照题目给定的方式生成。节点间连单向边,激励能按照边在 𝐷 时刻后传到下一个节点,然后每个节点根据当前的输入激励之和判断其是否大于等于 30,从而发生脉冲。
问 𝑇 秒后所有节点的情况,及所有神经元脉冲次数的最值。
题解:
解法一:
直接遍历。
每个时刻遍历一遍脉冲源,判断是否脉冲,遍历每个突触,记录传播的Ik。
Ik[i][j],表示第i个神经元第j时刻收到的脉冲强度。
每个时刻遍历一遍神经元,同上。
遍历T时刻的所有神经元求的结果。
时间复杂度$𝑂(T𝑛²)$,空间复杂度$𝑂(T𝑛)$,期望得分66。
1 |
|
解法二:
解法一的问题:依次遍历不同的突触寻找入结点的相应脉冲源和神经元,耗时过长。
优化:使用图结构存储突触的信息。
时间复杂度$O(T(N+S))$,空间复杂度$O(DN)$,期望得分100.
1 |
|
题目 4:收集卡片
题目描述:
𝑛 种卡牌,抽到 𝑖 的概率为 𝑝𝑖。如果已经抽过 𝑖 了,转换为一枚硬币。每 𝑘 个硬币可购买一张还未抽过的卡牌。
小林去抽卡,问在才去最优策略的情况下,期望需要抽的卡牌数量。绝对误差 ≤10⁻⁴。
𝑛 ≤ 16,1 ≤ 𝑘 ≤ 5。
题解:
动态规划、状压。
解法一:
$f_{i,j}$ 表示状态为 $i$ (二进制,1表示抽到,0没有抽到),已经抽了 $j$ 次的概率。
边界条件$f_{0,0}=1$。循环枚举。
外层循环枚举收集到的卡牌集合状态,初始状态为收集到了0张卡牌。
内层循环枚举当前持有卡牌数量。
如果当前已经收集了所有的卡牌,则计算答案,并将概率乘以持有卡牌数量加入结果。
否则,枚举每一种卡牌,若卡牌已经在集合里,则只需要把持有卡牌数量加1,并以当前概率计算附加一个卡牌的情况。
若卡牌不在集合里,则把卡牌加入集合,持有卡牌数量加1,并以当前概率计算新加入一个卡牌的情况。
时间复杂度$O(2^n n^2 k)$,空间复杂度为 $O(2^n nk)$,期望得分100。
1 |
|
题目 5:箱根山岳险天下
题目描述:
有一个集训队,每个人有排名,和权值。
有 𝑚 个事件,每个事件有 4 种类型:
- 将排名最后的一个人踢出集训队,但可一直随队训练。
- 在集训队中加入一个人,排名在末尾。
- 将在第 𝑠 天排名为 [𝑙, 𝑟] 的人的权值乘 y。
- 询问当前在第 𝑠 天排名为 [𝑙, 𝑟] 的人的权值和,对 𝑝 取模。
𝑚 ≤ 3 × 10⁵,2 ≤ p ≤ 2³⁰,mode ∈ 0,1。
题解:
解法一:
时间复杂度$𝑂(𝑚log^2 𝑚)$,空间复杂度$O(m)$,期望得分:40。
1 |
|
解法二:
参考解法:动态+树链剖分。
以排名为树的高度,以时间为序依次加点的树。对于删点就等于在时间上退到父亲,然后之后就在父亲的基础下再加点。
如果可以离线那么树剖维护,but在线那么改成LCT即可。期望得分100。
#include <bits/stdc++.h>
using namespace std;
int MOD;
int ad(int x, int y)
{
x += y;
return x >= MOD ? x - MOD : x;
}
int mu(int x, int y) { return 1ll * x * y % MOD; }
const int maxn = 3e5 + 5;
int ch[maxn][2], fa[maxn], fa_tru[maxn];
int sm[maxn], tot, A[maxn], rev[maxn], dep[maxn], lz[maxn];
vector<pair<int, int>> BC[maxn];
static bool isroot(int p) { return ch[fa[p]][0] != p && ch[fa[p]][1] != p; }
void putup(int p) { sm[p] = ad(ad(sm[ch[p][0]], sm[ch[p][1]]), A[p]); }
void rotate(int x)
{
int y = fa[x];
int z = fa[y];
fa[x] = z;
if (!isroot(y)) {
ch[z][ch[z][1] == y] = x;
}
int k = (ch[y][1] == x);
ch[y][k] = ch[x][k ^ 1];
fa[ch[y][k]] = y;
fa[y] = x;
ch[x][k ^ 1] = y;
putup(y);
putup(x);
}
void pushdown(int p)
{
if (rev[p]) {
swap(ch[p][0], ch[p][1]);
rev[ch[p][0]] ^= 1, rev[ch[p][1]] ^= 1;
rev[p] = 0;
}
if (lz[p] != 1) {
A[ch[p][0]] = mu(A[ch[p][0]], lz[p]);
A[ch[p][1]] = mu(A[ch[p][1]], lz[p]);
sm[ch[p][0]] = mu(sm[ch[p][0]], lz[p]);
sm[ch[p][1]] = mu(sm[ch[p][1]], lz[p]);
lz[ch[p][0]] = mu(lz[ch[p][0]], lz[p]);
lz[ch[p][1]] = mu(lz[ch[p][1]], lz[p]);
lz[p] = 1;
}
}
void splay(int x)
{
int y, z;
static int sta[maxn], top;
top = 0;
sta[++top] = x;
for (int i = x; !isroot(i); i = fa[i]) {
sta[++top] = fa[i];
}
while (top)
pushdown(sta[top--]);
while (!isroot(x)) {
y = fa[x];
z = fa[y];
if (!isroot(y))
(ch[z][0] == y) ^ (ch[y][0] == x) ? rotate(x) : rotate(y);
rotate(x);
}
}
void acc(int x)
{
for (int y = 0; x; y = x, x = fa[x]) {
splay(x);
ch[x][1] = y;
putup(x);
}
}
void setroot(int x)
{
acc(x);
splay(x);
rev[x] ^= 1;
}
void link(int x, int y)
{
setroot(x);
fa[x] = y;
}
int getp(int tim, int rank)
{
auto it = --upper_bound(BC[rank].begin(), BC[rank].end(),
make_pair(tim, 0x3f3f3f3f));
return it->second;
}
int M, T, NOW, LA;
int main()
{
scanf("%d%d%d", &M, &MOD, &T);
int OPT, S, L, R, Y;
for (int tim = 1; tim <= M; tim++) {
scanf("%d%d", &OPT, &S);
if (OPT == 1) {
if (T)
S = (S ^ LA);
if (S == 0) {
NOW = fa_tru[NOW];
} else {
++tot;
lz[tot] = 1;
fa_tru[tot] = fa[tot] = NOW;
dep[tot] = dep[NOW] + 1;
BC[dep[tot]].push_back(make_pair(tim, tot));
A[tot] = sm[tot] = S;
NOW = tot;
}
} else if (OPT == 2) {
scanf("%d%d%d", &L, &R, &Y);
if (T)
Y = Y ^ LA;
L = getp(S, L); // S 天 第L名
R = getp(S, R);
setroot(L);
acc(R);
splay(R);
A[R] = mu(A[R], Y);
sm[R] = mu(sm[R], Y);
lz[R] = mu(lz[R], Y);
} else {
scanf("%d%d", &L, &R);
L = getp(S, L);
R = getp(S, R);
setroot(L), acc(R), splay(R);
LA = sm[R];
printf("%d\n", LA);
}
}
return 0;
}