Summary on 2023.4.12

Reflection

暴露出的问题是独立思考的能力不足,数学的基础不劳,D 的能力有亿点弱.

主要就是思考能力欠缺,十分欠缺,以后多 VP 一点比赛吧.

个人感觉难度:T3 > T4 > T2 > T1,码力考察小,思考量也不是很大(但虐死我足够了).

不过思考能力有着明显的进步,这是好事,而且做题太少是不够的,以后要多做题.

inv[i] = (p - p / i) * inv[i % p] % p;

Solution

T1

会,不写了.

T2

看到一个对着一个图进行修改的东西直接上去分层图就行了,这个方法一定要掌握.

然后发现这是一个 01 最短路,然后我们上去 01BFS 即可解决问题.

Code 很简单,不写了.

T3

T3,看到整除考虑用质因子去思考.

有两种方法,但是本质是一样的.

法一

这个方法需要 da♂rk 力打表找规律,并且思路有一点阴间,更推荐法二.

先考虑简单的问题,假如我们只用一个质数的 $i$ 次幂作为最后的结尾的数的数列有多少个.

设 $dp_{i, j}$ 表示用 $i$ 次方,长度为 $j$ 的最后一个数列可以被 $p^i$ 表示的数列的个数.

andy_lz 是真强,这种逆天的状态都能想出来.

定住最后的 $a_j$,我们来思考如何递推:

  • 当$a_{j - 1} = p^i$ 的时候,发现定住了,那么这时候的数列数就只能是 $dp_{i, j - 1}$,因为数列本质上没有变化.
  • 当 $a_{j - 1} \ne p^i$ 的时候,发现这和 $a_{j} = p^{i - 1}$ 的情况下的数列是一样的.

然后我们得到递推式:
$$
dp_{i, j} = dp_{i - 1, j} + dp_{i, j - 1}
$$
现在我们处理好了固定次幂下的数列个数,考虑下一步:如何扩展到多个质因子的次幂相乘的结果?

发现对于同样的 $i$ 次幂,数列的个数是固定的,无论使用哪一个质因子做底数.(当这个指数在值域范围内的时候.)

那么我们就可以直接考虑值域内的每一个数,它最终可以贡献出多少贡献.

我们求完了 $i$ 次幂,然后我们直接考虑把 $x$ 这个数做结尾会贡献多少的数,而且它还会被不同的分解.

假如 $x = a^i \times b^j$ 的话:

以 $x$ 结尾的数的数列个数就是 $dp_{i, n} \times dp_{j, n}$,因为这样肯定最后能将 $x$ 乘出来,并且前边都是满足条件的数列.

然后我们就可以对于每一个数,看一下它可以分解为某个质因子的多少的幂次方,然后乘上对应的答案数,最后把每个数的贡献总和加到一起,这样就解决了这个问题.

Code
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
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#define INF 0x3f3f3f3f
typedef long long ll;
using namespace std;

ll rd() {
ll x = 0, w = 1;
char c = getchar();

while(c < '0' || c > '9') {
if(c == '-') w = -1;
c = getchar();
}

while(c >= '0' && c <= '9') {
x = x * 10 + (c - '0');
c = getchar();
}

return x * w;
}

const int N = 2e5 + 5;
const long long mod = 998244353;

ll dp[25][N];
int n, m;
ll sum[N], ans;

int tot;
ll pr[N];
bool np[N];

int main() {
// freopen("count.in", "r", stdin);
// freopen("count.out", "w", stdout);

n = rd(), m = rd();
for(int i = 1; i <= n; ++i)
dp[0][i] = 1;

for(int i = 1; i <= 20; ++i)
for(int j = 1; j <= n; ++j)
dp[i][j] = (dp[i][j - 1] + dp[i - 1][j]) % mod;

for(int i = 2; i * i <= m; ++i)
if(!np[i]) {
pr[++tot] = i;
for(int j = i * 2; j * j <= m; j += i)
np[j] = 1;
}

for(int i = 1; i <= m; ++i)
sum[i] = 1;

for(int i = 2; i <= m; ++i) {
int tmp = i, cnt = 0;
for(int j = 1; tmp > 1 && j <= tot; ++j) {
cnt = 0;
while( !(tmp % pr[j] ) )
tmp /= pr[j], ++cnt;

sum[i] = (sum[i] * dp[cnt][n]) % mod;
}

if(tmp > 1)
sum[i] = (sum[i] * dp[1][n]) % mod;
}

for(int i = 1; i <= m; ++i)
ans = (ans + sum[i]) % mod;

printf("%lld", ans);

fclose(stdin);
fclose(stdout);
return 0;
}

/*
3 4
20 30
200000 200000
*/

法二

想要达到互质的效果,我们不妨也考虑最后的数是哪一个数.

最后的数肯定可以分解为一个算术基本定理的形式,我们用这个做一点事情:

若干个质因子相乘,我们将这些质因子随机地放在前面的几个位置上,这是一个组合数问题,然后我们只需要将这些数从头到尾乘过来,不难满足题目要求.

然后我们只需要处理组合数,然后处理逆元,然后做完了.

这个思路还是比较好推的,更推荐这个做法.

Code

PS:此为 L_ndyz 的代码,博主懒得自己写一份了(,声明一下版权.

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
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<string>
#include<cstring>
#include<set>
#include<cmath>
#define int long long
using namespace std;
int n,m;
int p=998244353,ans=0;
long long inv[2000100],A[2000100];
long long C(int x,int y)
{
if(x>y) return 0;
return A[y]*inv[x]%p*inv[y-x]%p;
}
void sol(int now)
{
int sum=1;
for(int i=2;i*i<=now;i++)
{
if(now%i==0)
{
int cnt=0;
while(now%i==0)
{
now/=i;cnt++;
}
sum=sum*C(n-1,cnt+n-1)%p;
}
}
if(now>1)
{
int cnt=1;
sum=sum*C(n-1,cnt+n-1)%p;
}
ans=(ans+sum)%p;
}
signed main()
{
// freopen("gift.in","r",stdin);
// freopen("gift.out","w",stdout);
cin>>n>>m;
A[0]=A[1]=inv[0]=inv[1]=1;
for(int i=2;i<=n*5;i++)
{
inv[i]=(p-p/i)*inv[p%i]%p;
}
for(int i=2;i<=n*5;i++)
{
A[i]=(A[i-1]*i)%p;inv[i]=(inv[i-1]*inv[i])%p;
}
for(int i=1;i<=m;i++)
{
sol(i);
}
cout<<ans;
fclose(stdin);
fclose(stdout);
return 0;
}

T4

拿到异或,肯定是要拆位考虑的.

想要总和为 $m$,并且每一个二进制位上的 1 的个数都为偶数,不妨按照每一个二进制位考虑,这样能够很方便地保证偶数的限制.

因为一个二进制位会对应 $n$ 个下标位置,而这些下标位置我们是可以随意摆放的,也就是从 $n$ 个物品中选择 $i$ 个物品,这是一个组合数问题,使用杨辉三角 $\mathcal{O}(n^2)$ 即可处理组合数.

然后我们就可以快乐地开搜,然后我们接一个记忆化上去,这样就可以快速地解决这个问题了.

Code

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
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#define INF 0x3f3f3f3f
typedef long long ll;
using namespace std;

ll rd() {
ll x = 0, w = 1;
char c = getchar();

while(c < '0' || c > '9') {
if(c == '-') w = -1;
c = getchar();
}

while(c >= '0' && c <= '9') {
x = x * 10 + (c - '0');
c = getchar();
}

return x * w;
}

const int N = 5005;
const ll mod = 998244353;

ll dp[25][N];
ll n, m, C[N][N];

ll dfs(ll pos, ll res) {
if(dp[pos][res]) return dp[pos][res];

if(pos == 0) return dp[pos][res] = C[n][res];

for(ll i = 0; (1 << pos) * i <= res; i += 2)
dp[pos][res] = (dp[pos][res] + dfs(pos - 1, res - (1 << pos) * i) % mod * C[n][i] % mod) % mod;

return dp[pos][res];
}

int main() {
// freopen("gift.in", "r", stdin);
// freopen("gift.out", "w", stdout);

n = rd(), m = rd();
if(m % 2) printf("0");
else {
C[0][0] = 1;
for(int i = 1; i <= n; ++i) {
C[i][0] = 1;
for(int j = 1; j <= i; ++j)
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod;
}

printf("%lld\n", dfs(19, m));
}

// fclose(stdin);
// fclose(stdout);
return 0;
}

Somthing Else

  • 这次考试是我安排的.
  • 题目是我选的,但我不知道题目的具体内容.
  • 所以为什么 Ptilopsis_w 一个学图论的会出两个数学题啊.
  • 后来知道被骗了,这是 Jekyll_Y 的题.

Summary on 2023.4.12
http://dxrprime.github.io/2023/04/16/Summary on 2023.4.12/
作者
Shelter Prime
发布于
2023年4月16日
许可协议