Part 1:25 pts \texttt{Part 1:25 pts} Part 1:25 pts
这 25 25 25 分是 m = 0 m=0 m=0 的特殊情况。
m = 0 m=0 m=0,代表每一天都可以按计划学习,那么对于每一个单词直接模拟即可。如果你对数据范围不太敏感,建议数组都开大一点。
时间复杂度 O ( n k ) O(nk) O(nk)。
//pre表示当日学习单词数,re表示当日复习单词数
//maxday用来统计最后学习/复习单词的一天
int maxday = 0;
for(int i = 1;i <= n;i++) {
//对于每一个单词
pre[d[i]]++;
maxday = max(maxday,d[i]);
for(int j = 1;j <= k;j++) {
int reday = d[i] + t[j];
re[reday]++;
maxday = max(maxday,reday);
}
}
Part 2:65 pts \texttt{Part 2:65 pts} Part 2:65 pts
这 65 65 65 分是虽然有 m m m,但是可以直接通过暴力模拟。
把特殊的日子用一个 bool 数组存下来,如果哪一天学习单词/复习单词时,有特殊情况,就不断与后面交换,直到哪一天能学习为止。因为我们是将特殊情况在输入时直接处理,所以对后面的程序没有影响。
时间复杂度仍然是 O ( n k ) O(nk) O(nk)。
学习单词的特殊处理:
for(int i = 1;i <= m;i++) {
cin >> s[i];
spe[s[i]] = true;
}
for(int i = 1;i <= n;i++) {
if(spe[d[i]] == true) {
while(spe[d[i]] == true) d[i]++;
}
}
复习单词的特殊处理:
for(int i = 1;i <= n;i++) {
pre[d[i]]++;
maxday = max(maxday,d[i]);
for(int j = 1;j <= k;j++) {
int reday = d[i] + t[j];
if(spe[reday]) {
while(spe[reday]) reday++;
}
re[reday]++;
maxday = max(maxday,reday);
}
}
Part 3:100 pts \texttt{Part 3:100 pts} Part 3:100 pts
我们会发现,上一个程序中,最坏的运算次数会达到 1 0 6 × 1 0 3 = 1 0 9 10^6 \times 10^3=10^9 106×103=109 次,所以最后一个 Subtask 有两个点超时了。
(注:赛时的时限是 1.00 1.00 1.00s,把这两个点大力卡卡常,也许还能过;赛后为了卡掉错解,时限放到了 500 500 500ms,就完全不能过了。)
看看数据范围,我们从哪里入手呢?
仔细观察可以发现, d i d_i di 和 t i t_i ti 都是 ≤ 1 0 3 \le 10^3 ≤103 的,也就是说,总天数一定不会超过 2000 2000 2000。那么我们就可以按天记录答案,就可以将 1 0 6 10^6 106 的数量级降低至 2000 2000 2000。
由于 d i d_i di 单调不减,所以总时间复杂度为 O ( d n k ) O(d_nk) O(dnk)。
//isd[i]表示第i天要学几个词
for(int i = 1;i <= maxd;i++) {
if(isd[i] && spe[i]) {
int x = i;
while(spe[x]) {
isd[x+1] += isd[x];
isd[x] -= isd[x];
x++;
//这是交换操作
}
}
}
for(int i = 1;i <= maxd;i++) {
if(isd[i] > 0) {
pre[i] += isd[i];
for(int j = 1;j <= k;j++) {
int reday = i + t[j];
if(spe[reday]) {
while(spe[reday]) reday++;
}
re[reday] += isd[i];
maxday = max(maxday,reday);
}
}
}
运算次数最坏情况下为 1 0 3 × 1 0 3 = 1 0 6 10^3\times10^3=10^6 103×103=106。
Part 4 \texttt{Part 4} Part 4
总结:这一题并没有太大的思维难度,比较考察代码实现能力,建议评橙或者黄。