2221 算法第一次上机题解

A 傻逼题

应该没啥可写的,某人这么说的。

B 神奇的数列

题意

$Hn = \left{\begin{matrix} \Sigma^n{i=1} H{i-1}H{n-i} (n \ge 2) \ 1 (n=0,1) \end{matrix}\right.$ 求 $H_i$ 对 $998244353$ 取模的结果。

题解

离线计算出所有的 $H_i$,打表也行,直接输出即可。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <bits/stdc++.h>
using namespace std;

#define MAX_N 1000
#define MOD 998244353

unsigned long long h[1003];

int main()
{
h[0]=h[1]=1;
for (int i=2;i<=MAX_N;i++)
for (int j=1;j<=i;j++)
h[i]=(h[i]+(h[j-1]*h[i-j])%MOD)%MOD;
int q;
scanf ("%d",&q);
while (q--)
{
int n;
scanf ("%d",&n);
printf ("%llu\n",h[n]);
}
return 0;
}

C 电子对撞

题意

给出两个多项式,最高次分别为 $n,m$,求 $f(x,y)=(\Sigma{i=0}^n a_ix^i)(\Sigma{i=0}^m b_iy^i)$ 对 $10007$ 取模的结果。

题解

直接对每组数据上暴力即可,注意求 $a^i$ 时使用快速幂。 然后 TLE 了。 结果是用秦九韶。

快速幂代码

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
#include <bits/stdc++.h>
using namespace std;

#define MOD 10007

int n,m;
int a[30004],b[30004];

int quickPow(int a,int b)
{
int t=a;
int res=1;
while (b)
{
if (b&1)
res=(res*t)%MOD;
t=(t*t)%MOD;
b>>=1;
}
return res;
}

int calc(int *a,int n,int x)
{
int res=0;
for (int i=0;i<=n;i++)
res=(res+((a[i]%MOD)*quickPow(x,i))%MOD)%MOD;
return res;
}

int main()
{
scanf ("%d",&n);
for (int i=0;i<=n;i++)
scanf ("%d",&a[i]);
scanf ("%d",&m);
for (int i=0;i<=m;i++)
scanf ("%d",&b[i]);
int q;
scanf ("%d",&q);
while (q--)
{
int x,y;
scanf ("%d %d",&x,&y);
int res=(calc(a,n,x)*calc(b,m,y))%MOD;
printf ("%d\n",res);
}
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
#include <bits/stdc++.h>
using namespace std;

#define MOD 10007

int n,m;
int a[30004],b[30004];

int calc(int *a,int n,int x)
{
int res=0;
for (int i=n;i>=0;i--)
{
res=(res*x)%MOD;
res=(res+a[i])%MOD;
}
return res;
}

int main()
{
scanf ("%d",&n);
for (int i=0;i<=n;i++)
scanf ("%d",&a[i]);
scanf ("%d",&m);
for (int i=0;i<=m;i++)
scanf ("%d",&b[i]);
int q;
scanf ("%d",&q);
while (q--)
{
int x,y;
scanf ("%d %d",&x,&y);
int res=(calc(a,n,x)*calc(b,m,y))%MOD;
printf ("%d\n",res);
}
return 0;
}

D 新·电子对撞

题意

给出两个多项式,项数为 $n,m$,给出系数 $ai,b_i$ 与指数 $A_i,B_i$ 求 $f(x,y)=(\Sigma{i=0}^n aix^{A_i})+(\Sigma{i=0}^m b_ix^{B_i})$。

题解

直接用结构体把两个多项式放一起,根据指数 sort 然后合并即可。

代码

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
56
57
58
59
60
61
62
#include <bits/stdc++.h>
using namespace std;

int n,m;

struct polynomial
{
long long a;
int A;
};
polynomial a[200005];

bool cmp(polynomial a,polynomial b)
{
return a.A<b.A;
}

int main()
{
int T;
scanf ("%d",&T);
while (T--)
{
scanf ("%d",&n);
for (int i=1;i<=n;i++)
scanf ("%lld",&a[i].a);
for (int i=1;i<=n;i++)
scanf ("%d",&a[i].A);
scanf ("%d",&m);
for (int i=1;i<=m;i++)
scanf ("%lld",&a[n+i].a);
for (int i=1;i<=m;i++)
scanf ("%d",&a[n+i].A);
sort(a+1,a+n+m+1,cmp);
for (int i=1;i<=n+m;i++)
{
if (a[i].a==0)
continue;
for (int j=i+1;j<=n+m;j++)
{
if (a[i].A==a[j].A)
a[i].a+=a[j].a,a[j].a=0;
else
break;
}
}
int cnt=0;
for (int i=1;i<=n+m;i++)
if (a[i].a)
cnt++;
printf ("%d\n",cnt);
for (int i=1;i<=n+m;i++)
if (a[i].a)
printf ("%lld ",a[i].a);
puts("");
for (int i=1;i<=n+m;i++)
if (a[i].a)
printf ("%d ",a[i].A);
puts("");
}
return 0;
}

E 套娃!

题意

对每个 $a_i$,求数列 ${a_i}$ 中小等于它的数的个数。

题解

暴力循环即可。

代码

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
#include <bits/stdc++.h>
using namespace std;

int n;
int a[1003];

int main()
{
int T;
scanf ("%d",&T);
while (T--)
{
scanf ("%d",&n);
for (int i=1;i<=n;i++)
scanf ("%d",&a[i]);
for (int i=1;i<=n;i++)
{
int cnt=0;
for (int j=1;j<=i;j++)
if (a[j]<=a[i])
cnt++;
printf ("%d ",cnt);
}
puts("");
}
return 0;
}

F Elemental Creation

题意

给定 $n$,考虑这样的一个式子 $a +$($-$)$b = c (a,b,c \leq n)$,问有多少组有序数对 $(a,b,c)$ 能够满足这个等式。

题解

对于一个确定的 $c$,我们先计算符号为加号的情况。 显然 $a$ 最小为 ,也即 $0+c=c$,然后我们同时让加号左侧加一,右侧减一,得到$1+c-1=c$,以此类推,可以得到 $c+1$ 种情况。 再来考虑符号为减号的情况。同上我们可以得到 $c-0=c$,让减号左侧加一,右侧加一,得到 $c+1-1=c$,可以看出共 $n-c+1$ 种情况。 再知道 $c$ 的取值有 $n+1$ 种,根据乘法原理可知答案为 $(n+1)(n+2)$,注意开 long long 即可。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <bits/stdc++.h>
using namespace std;

int main()
{
int T;
scanf ("%d",&T);
while (T--)
{
long long n;
scanf ("%lld",&n);
printf ("%lld\n",(n+1)*(n+2));
}
return 0;
}

G Meteor Lights

题意

给出 $n$ 组 $(s_i, d_i)$ 与 $t$,问使得 $t-(s_i+kd_i)$ 最小的 $i$ 与 $s_i+kd_i$。

题解

对于一组 $(s_i, d_i)$,若 $s_i < t$ 则使得 $t-(s_i+kd_i)$ 最小的 $k$ 为 $\left \lfloor \frac{t-s_i}{d_i} \right \rfloor$ 或 $\left \lfloor \frac{t-s_i}{d_i} \right \rfloor + 1$,否则 $k=0$,带入计算即可。

代码

1
还没写

H 蛇的地铁

题意

没看

题解

不知道

代码

没写

I Alea jacta est!

题意

$2^n$ 只猫参加比赛,按顺序两两比赛,$p_i$ 大的获胜,所有获胜的再按顺序比赛,直到只剩一只。问每只猫所遇到的最大 $p_i$。

题解

按题意模拟即可。

代码

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
#include <bits/stdc++.h>
#define toat nowat^1
using namespace std;

int n,m;
int p[300000],meet[300000];

int main()
{
int T;
scanf ("%d",&T);
while (T--)
{
memset (meet,0,sizeof(meet));
scanf ("%d",&n);
m=1<<n;
for (int i=1;i<=m;i++)
scanf ("%d",&p[i]);
queue<int> q[2];
int nowat=0;
for (int i=1;i<=m;i++)
q[nowat].push(i);
for (int i=1;i<=n;i++,nowat^=1)
while (!q[nowat].empty())
{
int a=q[nowat].front();
q[nowat].pop();
int b=q[nowat].front();
q[nowat].pop();
meet[a]=max(meet[a],p[b]);
meet[b]=max(meet[b],p[a]);
if (p[a]>p[b])
q[toat].push(a);
else
q[toat].push(b);
}
for (int i=1;i<=m;i++)
printf ("%d ",meet[i]);
puts("");
}
return 0;
}

J Radiance

题意

给定 $n$,求不大于它的最大回文数。

题解

对于一个数 $n$,所求回文数的位数显然应该与 $n$ 一致(特殊情况 $100 \cdots 0$ 要特判)。

下面考虑一般情况,不难想到从外向内构造所求的回文数。 考虑所求回文数的高位,显然我们应该尽可能让高位(左边)不减。也就说,我们应该让回文数“中间”的部分弥补我们为了高位不减付出的代价,也就是说,让相对靠中间的那些数减小。具体可以预支多少呢?答案是除去靠近中间的全零块外的部分都可以预支,即优先保证高位不减,且对应的低位与高位相同(哪怕让低位变大),而这种行为将会在后面通过减小高位中的较低位来弥补。对于 $x1x_2 \cdots x_my_100 \cdots 0y_2z_mz{m-1} \cdots z_1(y_1^2 + y_2^2 \neq 0)$ 所有的 $z$ 都可以被预支,同时通过(可能)减小 $y_1$ 与较高位的 $z$ 来保证合法性。

在处理 $x$ 时什么时候要预支呢?答案是当对应的 $z$ 小于 $x$ 的时候,因为要让构造的数回文所以要调大 $z$。 那么什么时候可以补偿预支呢?答案是在预支之后,当处理到对应的 $z$ 大于 $x$ 的时候,因为调小较高位的 $z$ 使得我们所构造的数字再次合法了,等同于没有预支。

至此,我们可以看出,答案为 $x1x_2 \cdots x_m y_1’ \cdots y_2’ x_mx{m-1} \cdots x_1(yz \neq 0)$ 问题变成如何确定 $y_1’ \cdots y_2’$。

下面考虑如何构造 $y_1’ \cdots y_2’$。

假设在处理 $y$ 时没有“预支”: 若 $y_1 > y_2$ 答案显然为 $(y_1-1)999 \cdots 9 (y_1-1)$ 若 $y_1 \leq y_2$ 答案显然为 $y_1 00 \cdots 0 y_1$ 假设在处理 $y$ 时有“预支”: 若 $y_1 \ge y_2$ 答案显然为 $(y_1-1)999 \cdots 9 (y_1-1)$ 若 $y_1 < y_2$ 答案显然为 $y_1 00 \cdots 0 y_1$。

至此,我们成功构造出了最大的回文数。

赠送一组测试数据

样例输入

1
2
3
4
5
6
7
8
9
8
320
111000
510006
6
63
1242
100
10101

样例输出

1
2
3
4
5
6
7
8
313
110011
509906
6
55
1221
99
10101

代码

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#include <bits/stdc++.h>
using namespace std;

char s[5001];
int zerol,zeror;

bool spj(int st,int ed)
{
if (s[st]!='1')
return false;
for (int i=st+1;i<=ed;i++)
if (s[i]!='0')
return false;
return true;
}

void init(int len)
{
int l,r;
if (len&1)
l=r=((len-1)>>1);
else
r=(len>>1),l=r-1;
while (r<len)
{
if (s[l]!='0' s[r]!='0')
break;
else
l--,r++;
}
zerol=l,zeror=r;
}

int main()
{
int T;
scanf ("%d",&T);
while (T--)
{
scanf ("%s",s);
int len=strlen(s);
int l=0,r=len-1;
if (len==1)
{
puts(s);
continue;
}
if (spj(l,r))
{
for (int i=1;i<len;i++)
putchar('9');
puts("");
continue;
}
init(len);
int tag=0;
for(;l<=r;l++,r--)
{
if (s[l]>s[r])
s[r]=s[l],tag=1;
if (s[l]<s[r])
s[r]=s[l],tag=0;
if (l==zerol && r==zeror)
{
if (tag)
{
if (s[l]!='0')
{
if (s[l]>=s[r])
{
s[l]-=1,s[r]=s[l];
for (int i=l+1;i<r;i++)
s[i]='9';
}
else
s[r]=s[l];
}
else
s[r]='0';
}
else if (s[l]!='0')
{
if (s[l]>s[r])
{
s[l]-=1,s[r]=s[l];
for (int i=l+1;i<r;i++)
s[i]='9';
}
else
s[r]=s[l];
}
else
s[r]='0';
break;
}
}
puts(s);
}
return 0;
}