ABC-399(a-f)

ABC-399(a-f)

wlwhonest Lv1

总结为掉分,掉得不应该。

教训是下次多读几遍题目,知道题目要做什么

A

汉明码,直接找不同

B

给数字排序,打得有点繁琐了

C

并查集,打得也是有点繁琐。

重要思想:在并查集的一开始,把每一个看成一个连通块,每连接一条边,连通块就减少一个。

总结:相应的,最后用总点数减去剩下的连通块,就是连的边数

D

一开始直接看样例,没有懂样例的最后一个例子,本人也因没认真读懂题目,一直在找最小换座位次数。

题意:找到第一次出现和第二次出现都相邻的点对数

方法:开一个数组维护一个数第一次出现的位置,当数x第一次出现的位置,和x前一位数 y ,当且仅当 x和y第一次出现的位置相差1的时候才有统计答案。特殊地,(1) 如果y前一个位置的是他自己第一次出现的位置,这个时候的不能统计; (2) 如果刚统计完就是自己也不能统计;

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

using namespace std;

typedef pair<int,int> pii;

void solve(){

int n;
cin>>n;
vector<int> a(2*n+2);
for(int i=1;i<=2*n;i++) cin>>a[i];

int cnt=0;
vector<int> yi(n+1);
for(int i=1;i<=2*n;i++){

if(yi[a[i]]&&yi[a[i-1]]&&a[i-1]!=a[i]){
if(yi[a[i-1]]==i-1) continue;
if(i-1-yi[a[i-1]]==1) continue;
if(abs(yi[a[i]]-yi[a[i-1]])==1){
cnt++;
// cout<<a[i]<<' '<<yi[a[i]]<<' '<<yi[a[i-1]]<<endl;
}
}
if(!yi[a[i]]){
yi[a[i]]=i;
}
}
cout<<cnt<<endl;

}

signed main() {
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

int t;
cin>>t;
while(t--){
solve();
}

return 0;
}

F

感觉可以用二项式展开定理

image-20250330020542537

知识铺垫

  1. 求组合数

递推法

递推公式:

含义:从n个不同的书中选出m个的方案数,对于第一个数有 不选 两种决策,如果选,则从剩下的 n-1 个中选 m-1 个,如果不选,则从剩下的 n-1 个中选 m 个

示意图: 来自左上和上方

image-20250330022226324

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

using namespace std;

typedef pair<int,int> pii;

const int N=110;
int C[N][N];
const int mod=1e9+7;

signed main() {
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

int n;
cin>>n;

for(int i=0;i<=n;i++){
for(int j=0;j<=i;j++){
if(j==0) C[i][j]=1;
else C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
}
}

return 0;
}
  1. 快速幂 + 组合公式

板子,用于大幂摸与乘法逆元

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>

using namespace std;

#define int long long
typedef pair<int,int> pii;

//快速幂板子
int qui(int a,int k,int p){
int res=1;
while(k){
if(k&1) res=res*a%p;
k>>=1;
a=a*a%p;
}
return res;
}

const int N=1e5+10;
//f[N] 用于存阶乘
//g[N] 用于存阶乘逆元
int f[N],g[N];
const int mod=1e9+7;


signed main() {
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

int n,m;
cin>>n>>m;

g[0]=f[0]=1;
for(int i=1;i<=n;i++){
f[i]=f[i-1]*i%mod;
g[i]=g[i-1]*qui(i,mod-2,mod)%mod;
}

// 组合数
cout<< f[n]*g[m]%mod*g[n-m]%mod;

return 0;
}
  1. 卢卡斯定理

待写…….

解决问题

题意:

思路:用组合意义取k次幂,二项式定理。

类似:NOI2009管道取珠

思路@wjh

以第i个元素结尾的子数组和

每增加一个元素会产生什么影响

请观察如下式子:

下列每个元素 表示从下标 的子数组的和,以 为例

观察上式我们可以思考:

从第二层开始,当前层能否由上一层得来,由此,我们对式子进行变形:

我们继续思考对每个元素计算k次方,当前层会比上一层多出什么:

以第4层作为当前层(第4个元素结尾构成的子数组和有四种):

发现第四层比第三层多了一项[4,4]不好整体转移

第4个元素结尾构成的子数组和的==k次幂==有四种:

我们先不看

以第4层作为当前层

令上一层子数组和的==k次幂==的 和为 ;

令当前层子数组和的==k次幂==的 和为 ;

由二项式展开定理,我们可以得到:

我们以i元素结尾的所有子数组和的k次幂的和作为状态:

状态转移方程可以写成如下:

上一个引用块的第3步用到了二项式展开定理,因此在代码中体现为多了m这种循环

这个式子意味着需要保留以第i个元素结尾的子数组和的1到k次幂的和的状态。

具体如下:

也就是:

是被提前算出来的 b1[0]

是被提前算出来的 b1[1]

是被提前算出来的 b1[2]

也就是以第三个元素结尾的子数组的和的1-2次幂 分别和 的2-1次幂相乘

也就是:

在代码中体现为:

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

using namespace std;

typedef pair<int,int> pii;
#define int long long

const int N=2e5+10,M=11;
const int mod=998244353;
int a[N],ap[N];
int c[M][M];
int dp[N][M];

signed main() {
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

int n,k=10;
cin>>n>>k;

for(int i=1;i<=n;i++){
cin>>a[i];
a[i]%=mod;
}

// 预处理组合数
for(int i=0;i<=k;i++){
for(int j=0;j<=i;j++){
if(j==0) c[i][j]=1;
else c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
}
}
//res统计答案
int res=0;
for(int i=1;i<=n;i++){
// 每一层元素的幂次
ap[0]=1;
for(int j=1;j<=k;j++){
ap[j]=ap[j-1]*a[i]%mod;
}

for(int j=0;j<=k;j++){
int sum=0;
for(int m=0;m<=j;m++){
int x=c[j][m]%mod;
// cout<<x<<endl;
// sum=(sum+c[j][m]*dp[i-1][m])%mod;
// sum=(sum+ap[j-m])%mod;
x=(x*dp[i-1][m])%mod;
x=(x*ap[j-m])%mod;
sum=(sum+x)%mod;
}
dp[i][j]=(sum+ap[j])%mod;
}
res=(res+dp[i][k])%mod;
}

// for(int i=1;i<=n;i++){
// for(int j=0;j<=k;j++){
// cout<<dp[i][j]<<' ';
// }
// cout<<endl;
// }
// cout<<endl;

cout<<res<<endl;

return 0;
}
  • Title: ABC-399(a-f)
  • Author: wlwhonest
  • Created at : 2025-03-31 23:50:18
  • Updated at : 2025-04-05 06:07:24
  • Link: https://blog.wlwhonest.top/2025/03/31/ABC-399-a-f/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments
On this page
ABC-399(a-f)