这次考试很不错总分 358 排名第一!
T1
这道题目,就是一道模拟,但是我考场上犹豫了一下,因为算了一下复杂度感觉可能会超时?但是老师说这道题目不要想复杂就是一道模拟,我就提交上去了。
题目也很简单直接放上题解。
题解
网页版:模考T1题解
这道题目就是一道模拟题目。
首先呢,我们要确定如何判断是否为 s 的子序列。
我的思路就是将给定的字符串 c 的每一位和 s 的每一位遍历一遍。
如果有相同那么我们将记录 c 的下标 j 加一,以便后面继续查找,并且这个 j 还起到一个作用就是统计 c 与 s 有几位相同,因为每次找到一个相同的我们就会将 j 加一。
最后我们判断 j 是否等于 c 的长度就好了。
伪代码:
bool check(string t){//检查函数
int i=0,j=0;//i代表s的下标,j代表t的下标同时统计相同的数目
while(/*如果两个下标均没有查过字符串的长度*/){
if(s[i]==t[j])j++;//如果一样则将j加一
i++;
}
return j==t.length();//判断j是否与t的长度相同
}
T2
这道题目一眼数学,推容斥,然后推了许久,所有方案数挺简单求的,但是不满足条件的就比较难了。之前写的代码样例没有过,比赛最后几分钟调了一下样例过了,赶快提交!
这道题目我推的可能有点复杂,别人的 AC 代码都比较简单,我分了两种情况,还分块解决了,好麻烦 qwq
题解
这道题目暴力的话是会超时的,所以我们想到数学优化:正难则反。
我们先求出所有方案数再减去不合法的就是合法的方案数。
首先总方案数比较好求用隔板法,就是我们将 L 分给不同的三个数,可以不分给一个数,那么为了解决可以不分给一个数的情况,我们可以将 L 加 3 加上的这三个可以理解为“假的”也就是你分到了这“假的”数,你的数值是不会增加了,然后我们要分给 3 个数,方案数就是: C^3_{L+3} 。
那么,不合法的数呢?大家都知道三角形两边之和大于第三边,于是我们可以得出三种不合法情况 (a+x)≥(b+y)+(c+z) 和 (b+y)≥(a+x)+(c+z) 和 (c+z)≥(a+x)+(b+y) 这里的 x,y,z 分别表示给 a,b,c 加上的数。
接下来我们以 (a+x)≥(b+y)+(c+z) 为例:我们可以变换一下不等式也就变成了 x ≥ y+z + (b+c-a) 这个时候会分成两种情况:
- k = b+c-a ≥ 0 这个时候 k 为非负数直接枚举 y+z 的值。
- 当 k<0 这个时候条件变成 x ≥ y+z - |k| 这个时候我们需要分块处理。
好了,现在就可以写出大体代码框架了:
#include<bits/stdc++.h>
#define I using
#define AK namespace
#define IOI std
#define i_ak return
#define ioi 0
#define int long long
I AK IOI;
int a,b,c,l,sum,ans;
int check1(int L,int k){
/*当k为非负数的时候*/
}
int check2(int L,int k){
/*当k为负数的时候*/
}
int f(int k,int L){
if(k>=0){
if(L<k)return 0;//如果L小于K直接返回 0
else return check1(L,k);当k为为负数时枚举k+z
}
else return check2(L,-k);//当k为负数的时候分成2快理
}
signed main(){
/*输入*/
sum=(l+3)*(l+2)*(l+1)/6;//总方案数 C^{3}_{L+3}
int k1=b+c-a,k2=a+c-b,k3=a+b-c;
int sum1=f(k1,l),sum2=f(k2,l),sum3=f(k3,l);//三种不合法的情况的数目
ans=sum-(sum1+sum2+sum3);//容斥原理这就是最后的答案
/*输出*/
i_ak ioi;
}
分析一下复杂度:
- 时间复杂度: O(L) 不会超市!放心提交!
- 空间复杂度: O(1) 没有额外空间。
T3
这道题目,以为挺简单的想了个类似于贪心的思路套了一个 map
然后就提交上去了,显然 贪心是假的 拿了 62 分,老师说正解应该是搜索,并且还需要搜索优化。
题解
网页版:
正在努力ing马上更新(好累呀不想写了 qwq
T4
这道题目有点思维量,数学老师的话就在我的耳边回响“正难则反”于是我考虑从终点往回推。这道题目码量还是挺小的,但是思维难度比较大,有点类似于贪心。
题解
我们可以逆向思考从终点反向推导。
可以发现,每一段运输过程中,我们需要保证下一段能继续运输且尽量装满。例如,当运输次数为 1 次时,从补给点到下一位置 d_1 运输距离 内消耗的苹果数加上到达该位置剩余的苹果数要等于 K ;当运输次数为 3 次(往返运输)时,运输距离 d_2 内消耗的苹果数加上到达该位置剩余的苹果数要等于 2K ,以此类推。
那么我们可以用 l 来存储还剩下的距离, a 记录消耗的苹果总数, t 表示当前阶段的运输次数。
每次循环我们要计算每次运输能前进的最大距离 maxn ,即 \frac{K}{2t-1} 其中 2t-1 表示在该阶段运输苹果需要走的单程次数。
- 如果
maxn
大于等于l
,说明当前阶段可以直接到达目的地,此时消耗的苹果数为l*(2*t-1)
,并跳出循环。 - 否则,不能直接到达,设置补给点,消耗的苹果数为
maxn*(2*t-1)
,更新剩余距离l
为l-maxn
,并将运输次数t
加 1 。
最后对 a 向上取整就是答案,即 ceil(a)
但是大家会发现这样 AC 不了,这是为什么呢?
因为我们还需要加上 (int)ceil(a)
否则输出会带数学符号输出会 WA!
代码框架:
#include<bits/stdc++.h>
#define I using
#define AK namespace
#define IOI std
#define i_ak return
#define ioi 0
I AK IOI;
int n,k;
int main(){
/*输入*/
double l=n,a=0;
int t=1;
while(l>0){
double maxn=k*1.0/(2*t-1);//运输能前进的最大距离
if(maxn>=l){//如果可以直接到达目的地
a+=l*(2*t-1);
break;
}
else{
/*更新*/
}
}
/*输出答案*/
i_ak ioi;
}
复杂度:
- 时间复杂度: O(\frac{N}{K})
- 空间复杂度: O(1)
总结
这次考得不错,算法没有难度主要是思维,以后要注意一点当要转类型的时候要强转否则会像今天 T4 一样失分 qwq
我的时间分配
首先花了半小时通读了题目,然后先做了 T3 大约花了十分钟编完了代码,然后去看了 T1。
T1 写模拟,算了一下复杂度害怕超时没敢提交,于是去看了 T2,T2 一眼数学,但是想了一会没啥思路,先去看了 T4,T4 是一道思维题,想了很久,听到老师说 T1 是模拟,去做了 T1.
T1 大约花了十分钟,然后重新看 T4,想到“正难则反”写出了代码,没过样例,这道题目大概花了 40 分钟调试,然后去看 T2.
老师提示 T2 用容斥,推了一下公式 推了 40 分钟都没有推出来,于是将零零散散得思路写到一起,样例没有过先骗分。
考试最后几分钟样例过了,赶快提交!AC 了!
T1预期:50?实际:100
T2预期:100?实际:100
T3预期:50? 实际:62
T4预期:100?实际:96