考完了CSP之后,自然是打算写题解了,因为今年没参加S组qwq,所以就只写了J组的TJ。

T1 apple

题目

点击展开题目

题目描述

小 Y 的桌子上放着 nn 个苹果从左到右排成一列,编号为从 11nn

小苞是小 Y 的好朋友,每天她都会从中拿走一些苹果。

每天在拿的时候,小苞都是从左侧第 11 个苹果开始、每隔 22 个苹果拿走 11 个苹果。随后小苞会将剩下的苹果按原先的顺序重新排成一列。

小苞想知道,多少天能拿完所有的苹果,而编号为 nn 的苹果是在第几天被拿走的?

输入格式

输入的第一行包含一个正整数 nn,表示苹果的总数。

输出格式

输出一行包含两个正整数,两个整数之间由一个空格隔开,分别表示小苞拿走所有苹果所需的天数以及拿走编号为 nn 的苹果是在第几天。

样例 #1

样例输入 #1

1
8

样例输出 #1

1
5 5

提示

【样例 11 解释】

小苞的桌上一共放了 88 个苹果。
小苞第一天拿走了编号为 114477 的苹果。
小苞第二天拿走了编号为 2266 的苹果。
小苞第三天拿走了编号为 33 的苹果。
小苞第四天拿走了编号为 55 的苹果。
小苞第五天拿走了编号为 88 的苹果。

【样例 22

见选手目录下的 apple/apple2.in 与 apple/apple2.ans。

【数据范围】

对于所有测试数据有:1n1091\leq n\leq 10^9

测试点nn\leq特殊性质
121\sim 21010
353\sim 510310^3
676\sim 710610^6
898\sim 910610^6
101010910^9

特殊性质:小苞第一天就取走编号为 nn 的苹果。

题解

还是很简单的一个找规律题。对于第一问,可以发现的是,每隔两个拿一个苹果的话,是相当于以 3 个苹果为一组,剩下不够 3 个的也当一组,每一次拿走一组中的第一个就可以了。

n=8n=8 时,图就是这样子的:

第二问呢?

根据上图可以发现,当 i=5i=5 时,nmod3=1n \mod 3=1,此时最后一组的第一个就是第 nn 个苹果了,直接输出 ii 就好。

代码

这是我的考场代码(增加了一些注释,有些是考场发癫写的):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <bits/stdc++.h>
//能出1=么,能出300么qwq
//CQ-J00273 LYX
//签到水题,找规律:(
using namespace std;
int main(){
freopen("apple.in","r",stdin);
freopen("apple.out","w",stdout);
long long n,i=0,ans=-1; //ans=-1标记ans未更新过
cin>>n;
while(n){
if(n%3==1&&!~ans) ans=i+1; //!~ans判断ans是否为-1,如果未更新过ans,此时的i就是取到第n个苹果的时间戳
n-=ceil(n*1.0/3),i++; //减掉3个一组第一个的苹果个数
}
cout<<i<<" "<<ans<<endl;
//小知识:我平常不加endl,但是Linux下是没有自动换行的,看起来很难受。
return 0;
}

T2 road

题面

点击查看题面

题目描述

小苞准备开着车沿着公路自驾。

公路上一共有 nn 个站点,编号为从 11nn。其中站点 ii 与站点 i+1i + 1 的距离为 viv_i 公里。

公路上每个站点都可以加油,编号为 ii 的站点一升油的价格为 aia_i 元,且每个站点只出售整数升的油。

小苞想从站点 11 开车到站点 nn,一开始小苞在站点 11 且车的油箱是空的。已知车的油箱足够大,可以装下任意多的油,且每升油可以让车前进 dd 公里。问小苞从站点 11 开到站点 nn,至少要花多少钱加油?

输入格式

输入的第一行包含两个正整数 nndd,分别表示公路上站点的数量和车每升油可以前进的距离。

输入的第二行包含 n1n - 1 个正整数 v1,v2vn1v_1, v_2\dots v_{n-1},分别表示站点间的距离。

输入的第三行包含 nn 个正整数 a1,a2ana_1, a_2 \dots a_n,分别表示在不同站点加油的价格。

输出格式

输出一行,仅包含一个正整数,表示从站点 11 开到站点 nn,小苞至少要花多少钱加油。

样例 #1

样例输入 #1

1
2
3
5 4
10 10 10 10
9 8 9 6 5

样例输出 #1

1
79

提示

【样例 1 解释】

最优方案下:小苞在站点 11 买了 33 升油,在站点 22 购买了 55 升油,在站点 44 购买了 22 升油。

【样例 2】

见选手目录下的 road/road2.in 与 road/road2.ans。

【数据范围】

对于所有测试数据保证:1n1051 \leq n \leq 10^51d1051 \leq d \leq 10^51vi1051 \leq v_i \leq 10^51ai1051 \leq a_i \leq 10^5

测试点nn \leq特殊性质
151\sim 588
6106\sim 1010310^3
111311\sim 1310510^5A
141614\sim 1610510^5B
172017\sim 2010510^5
  • 特殊性质 A:站点 11 的油价最低。
  • 特殊性质 B:对于所有 1i<n1 \leq i < nviv_idd 的倍数。

题解

这道题思维难度稍高一点,需要一种贪心思想。

贪心思想大概如此:不按照原来的思路。先在第一个站加油到达第二个站,如果第二个站便宜就加第二个,不然还是加第一站的油,到了第三站之后也是在前三个站中找最便宜的加油去第四站,以此类推。

简单一点说就是对于第 ii 个加油站,把加油的单价替换为 amin{a}a_{\min{\{a\}}}

然后模拟距离即可,注意油量和取整的问题。以及,一定要开 long longdouble,不然你会死得很惨!(洛谷死的是75pts)

代码

下面是我的代码,包括考试发癫和后续加的注释。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <bits/stdc++.h>
//qwq 差点挂掉
#define int long long //不开longlong见祖宗
using namespace std;
int n,d,v[114514],a[114514],minn=1e9,ans; //两个数组作用如题,注意实际上a_n项是没有用的(因为都到了第n个站了就不需要再加油去其他地方了)
double yl=0;
signed main(){
ios::sync_with_stdio(0);
freopen("road.in","r",stdin);
freopen("road.out","w",stdout);
cin>>n>>d;
for(int i=1;i<n;i++) cin>>v[i];
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<n;i++){ //注意 i<n,小学植树问题(
minn=min(a[i],minn); //替换加油价格
yl-=v[i-1]*1.0/d; //计算到了这个站的剩余油量
ans+=ceil((v[i]-yl*d)/d)*minn; //计算加油量并算钱
yl+=ceil((v[i]-yl*d)/d);
}
cout<<ans<<endl;
return 0;
}

T3 uqe

题面

点击查看题面

题目背景

众所周知,对一元二次方程 ax2+bx+c=0,(a0)ax ^ 2 + bx + c = 0, (a \neq 0),可以用以下方式求实数解:

  • 计算 Δ=b24ac\Delta = b ^ 2 - 4ac,则:
    1. Δ<0\Delta < 0,则该一元二次方程无实数解。
    2. 否则 Δ0\Delta \geq 0,此时该一元二次方程有两个实数解 x1,2=b±Δ2ax _ {1, 2} = \frac{-b \pm \sqrt \Delta}{2a}

例如:

  • x2+x+1=0x ^ 2 + x + 1 = 0 无实数解,因为 Δ=124×1×1=3<0\Delta = 1 ^ 2 - 4 \times 1 \times 1 = -3 < 0
  • x22x+1=0x ^ 2 - 2x + 1 = 0 有两相等实数解 x1,2=1x _ {1, 2} = 1
  • x23x+2=0x ^ 2 - 3x + 2 = 0 有两互异实数解 x1=1,x2=2x _ 1 = 1, x _ 2 = 2

在题面描述中 aabb 的最大公因数使用 gcd(a,b)\gcd(a, b) 表示。例如 12121818 的最大公因数是 66,即 gcd(12,18)=6\gcd(12, 18) = 6

题目描述

现在给定一个一元二次方程的系数 a,b,ca, b, c,其中 a,b,ca, b, c 均为整数且 a0a \neq 0。你需要判断一元二次方程 ax2+bx+c=0a x ^ 2 + bx + c = 0 是否有实数解,并按要求的格式输出。

在本题中输出有理数 vv 时须遵循以下规则:

  • 由有理数的定义,存在唯一的两个整数 ppqq,满足 q>0q > 0gcd(p,q)=1\gcd(p, q) = 1v=pqv = \frac pq

  • q=1q = 1则输出 {p},否则输出 {p}/{q},其中 {n} 代表整数 nn 的值;

  • 例如:

    • v=0.5v = -0.5 时,ppqq 的值分别为 1-122,则应输出 -1/2
    • v=0v = 0 时,ppqq 的值分别为 0011,则应输出 0

对于方程的求解,分两种情况讨论:

  1. Δ=b24ac<0\Delta = b ^ 2 - 4ac < 0,则表明方程无实数解,此时你应当输出 NO

  2. 否则 Δ0\Delta \geq 0,此时方程有两解(可能相等),记其中较大者为 xx,则:

    1. xx 为有理数,则按有理数的格式输出 xx

    2. 否则根据上文公式,xx 可以被唯一表示为 x=q1+q2rx = q _ 1 + q _ 2 \sqrt r 的形式,其中:

      • q1,q2q _ 1, q _ 2 为有理数,且 q2>0q _ 2 > 0
      • rr 为正整数且 r>1r > 1,且不存在正整数 d>1d > 1 使 d2rd ^ 2 \mid r(即 rr 不应是 d2d ^ 2 的倍数);

    此时:

    1. q10q _ 1 \neq 0,则按有理数的格式输出 q1q _ 1,并再输出一个加号 +
    2. 否则跳过这一步输出;

    随后:

    1. q2=1q _ 2 = 1,则输出 sqrt({r})
    2. 否则若 q2q _ 2 为整数,则输出 {q2}*sqrt({r})
    3. 否则若 q3=1q2q _ 3 = \frac 1{q _ 2} 为整数,则输出 sqrt({r})/{q3}
    4. 否则可以证明存在唯一整数 c,dc, d 满足 c,d>1,gcd(c,d)=1c, d > 1, \gcd(c, d) = 1q2=cdq _ 2 = \frac cd,此时输出 {c}*sqrt({r})/{d}

    上述表示中 {n} 代表整数 {n} 的值,详见样例。

    如果方程有实数解,则按要求的格式输出两个实数解中的较大者。否则若方程没有实数解,则输出 NO

输入格式

输入的第一行包含两个正整数 T,MT, M,分别表示方程数和系数的绝对值上限。

接下来 TT 行,每行包含三个整数 a,b,ca, b, c

输出格式

输出 TT 行,每行包含一个字符串,表示对应询问的答案,格式如题面所述。

每行输出的字符串中间不应包含任何空格

样例 #1

样例输入 #1

1
2
3
4
5
6
7
8
9
10
9 1000
1 -1 0
-1 -1 -1
1 -2 1
1 5 4
4 4 1
1 0 -432
1 -3 1
2 -4 1
1 7 1

样例输出 #1

1
2
3
4
5
6
7
8
9
1
NO
1
-1
-1/2
12*sqrt(3)
3/2+sqrt(5)/2
1+sqrt(2)/2
-7/2+3*sqrt(5)/2

提示

【样例 #2】

见附件中的 uqe/uqe2.inuqe/uqe2.ans

【数据范围】

对于所有数据有:1T50001 \leq T \leq 50001M1031 \leq M \leq 10 ^ 3a,b,cM|a|,|b|,|c| \leq Ma0a \neq 0

测试点编号MM \leq特殊性质 A特殊性质 B特殊性质 C
1111
222020
3310310 ^ 3
4410310 ^ 3
5510310 ^ 3
6610310 ^ 3
7,87, 810310 ^ 3
9,109, 1010310 ^ 3

其中:

  • 特殊性质 A:保证 b=0b = 0
  • 特殊性质 B:保证 c=0c = 0
  • 特殊性质 C:如果方程有解,那么方程的两个解都是整数。

思路

按照题目思路模拟分解即可,具体注释见代码。

大坑!

!注意!,请!注意!时间!复杂度!,这是我的考场代码,复杂度为 O(Tb24Tac)O(Tb^2-4Tac),无法通过:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
 #include <bits/stdc++.h>
//蒟蒻的垃圾模拟代码
//CCF当点人罢(
//J组题这么逆天
using namespace std;
int t,m;
inline void yls(int u,int d){
if((u<0&&d>0)||(u>0&&d<0)) cout<<'-';
if(d/__gcd(u,d)==1) cout<<abs(u/__gcd(u,d));
else cout<<abs(u/__gcd(u,d))<<'/'<<abs(d/__gcd(u,d));
}
inline bool wqpf(int tr){
return pow(floor(sqrt(tr)),2)==tr;
}
signed main(){
ios::sync_with_stdio(0);
freopen("uqe.in","r",stdin);
freopen("uqe.out","w",stdout);
cin>>t>>m;
for(int i=1,a,b,c;i<=t;i++){
cin>>a>>b>>c;int tr=b*b-4*a*c;
if(tr<0) cout<<"NO"<<endl;
else
if(wqpf(tr)){
if((sqrt(tr)-b)/(2*a)>(-sqrt(tr)-b)/(2*a))
yls(sqrt(tr)-b,2*a);
else yls(-sqrt(tr)-b,2*a);
cout<<endl;
}
else{
if((-b)*1.0/(a*2)) yls(-b,a*2),cout<<'+';
int ys=1;
for(int i=1;i<=tr;i++)
if(!(tr%i)&&wqpf(i)) ys=max(ys,i); //这里暴力开方浪费了很多时间,考场盲目自信复杂度算错了
tr/=ys,ys=sqrt(ys);
if((-sqrt(tr)*ys-b)/(2*a)>(sqrt(tr)*ys-b)/(2*a)) ys=-ys;
if(ys==a*2) cout<<"sqrt("<<tr<<")"<<endl;
else if(!(ys%(a*2))) cout<<abs(ys/(a*2))<<"*sqrt("<<tr<<")"<<endl;
else if(!(a*2%ys)) cout<<"sqrt("<<tr<<")/"<<abs(a*2/ys)<<endl;
else cout<<abs(ys/__gcd(ys,a*2))<<"*sqrt("<<tr<<")/"<<abs(a*2/__gcd(ys,a*2))<<endl;
}
}
return 0;
}

代码

可以看看注释,非常详细。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <bits/stdc++.h>
//修改过的代码,复杂度O(Tb)
using namespace std;
int t,m;
inline void yls(int u,int d){ //输出分子为u,分母为d的有理分数
if((u<0&&d>0)||(u>0&&d<0)) cout<<'-'; //异号,值为负
//除gcd可以达到化简分数的效果
if(d/__gcd(u,d)==1) cout<<abs(u/__gcd(u,d)); //分母为1,输出整数形式
else cout<<abs(u/__gcd(u,d))<<'/'<<abs(d/__gcd(u,d)); //按照分数形式输出
}
inline bool wqpf(int tr){
return pow(floor(sqrt(tr)),2)==tr; //判断一个整数tr是否为完全平方数,即为判断根号是否为整
}
signed main(){
ios::sync_with_stdio(0);
freopen("uqe.in","r",stdin);
freopen("uqe.out","w",stdout); //考试的时候不要写错了啊qwq
cin>>t>>m;
for(int i=1,a,b,c;i<=t;i++){
cin>>a>>b>>c;int tr=b*b-4*a*c; //计算解的根号部分的被开方数
if(tr<0) cout<<"NO"<<endl; //实数范围内无解
else
if(wqpf(tr)){ //根号部分是完全平方数,即有有理数解
if((sqrt(tr)-b)/(2*a)>(-sqrt(tr)-b)/(2*a)) //寻找最大解
yls(sqrt(tr)-b,2*a); //套公式即可
else yls(-sqrt(tr)-b,2*a);
cout<<endl;
}
else{
if((-b)*1.0/(a*2)) yls(-b,a*2),cout<<'+';
// 根据乘法分配律可得(-b+sqrt(tr))/2a=-b/2a-sqrt(tr)/2a
// 即为输出解的前一项b/2a,为有理数。
int ys=1,rr=tr,cnt; //tr为根号下三角形的被开方数,ys为被开方数的最大完全平方因数
//rr作临时变量,对根式进行化简,提系数
for(int i=2;i*i<=rr;i++)
if(!(rr%i)){ //找质因数
cnt=0;
while(!(rr%i)) rr/=i,cnt++; //化简至rr与i互质,cnt即为原数所含质因数i的个数
ys*=pow(i,cnt-cnt%2); //提偶数个i出来
}
tr/=ys,ys=sqrt(ys); //化简,提出来
if((-sqrt(tr)*ys-b)/(2*a)>(sqrt(tr)*ys-b)/(2*a)) ys=-ys;
//如果-b/2a-sqrt(tr)/2a是最大解时,把提出来的系数取负,直接就可以当做-b/2a+sqrt(tr)/2a
if(ys==a*2) cout<<"sqrt("<<tr<<")"<<endl;
//系数为1时,直接输出根号格式
else if(!(ys%(a*2))) cout<<abs(ys/(a*2))<<"*sqrt("<<tr<<")"<<endl;
//系数为整数时。注意:可以证明 sqrt(tr)/2a 一定为整,为了方便符号处理,直接取绝对值就好了
else if(!(a*2%ys)) cout<<"sqrt("<<tr<<")/"<<abs(a*2/ys)<<endl;
//系数分子为1,当作除法处理
else cout<<abs(ys/__gcd(ys,a*2))<<"*sqrt("<<tr<<")/"<<abs(a*2/__gcd(ys,a*2))<<endl;
//系数为分数时,按照题面格式输出为a*sqrt(c)/b的格式
}
}
return 0;
}

T4 bus

咕咕咕。。。qwq