第九届蓝桥杯国赛C++ B B B组口胡题解
第一题:简单数学,不讲了。
第二题:暴力位运算或者 d p [ i ] [ 2 ] dp[i][2] dp[i][2]递推。
第三题: l o w b i t ( ) lowbit() lowbit()求出最低位的1然后就左移异或一下。
第四题:每步走 1 1 1或 k k k,问从0走到 [ 0 , n − 1 ] [0,n-1] [0,n−1]的最大步数(环形) , b f s bfs bfs一下即可。
第五题: d p dp dp的好题,有时间写下代码。
U
p
d
:
Upd:
Upd:
前缀和优化
d
p
dp
dp。
令
d
p
[
i
]
[
j
]
[
k
]
dp[i][j][k]
dp[i][j][k]表示第
i
i
i层放了从
[
j
,
k
]
[j,k]
[j,k]块的方案数。
f
u
n
(
x
1
,
y
1
,
x
2
,
y
2
)
fun(x_1,y_1,x_2,y_2)
fun(x1,y1,x2,y2)表示前一层(也就是第
i
−
1
i-1
i−1层)有多少满足条件的状态能够递推到
d
p
[
i
]
[
j
]
[
k
]
dp[i][j][k]
dp[i][j][k],是一个矩阵的形式,所以可以用二维前缀和优化。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=105,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb push_back
int n,m;
int a[N][N];
ll s[N][N];
//a[i][j]计算第i层 前j个数有多少个X
ll dp[N][N][N];
void pre(int i){
for(int j=1;j<=m;j++)
for(int k=1;k<=m;k++)
s[j][k]=(s[j-1][k]+s[j][k-1]-s[j-1][k-1]+dp[i][j][k])%mod;
}
ll fun(int x1,int y1,int x2,int y2){
return (s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]+s[x1-1][y1-1])%mod;
}
int main(){
scanf("%d%d",&n,&m);
char str[N];
for(int i=n;i;i--){
scanf("%s",str+1);
for(int j=1;j<=m;j++)
a[i][j]=a[i][j-1]+(str[j]=='X');
}
dp[0][1][m]=1;
pre(0);
ll ans=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
for(int k=j;k<=m;k++){
if(a[i][k]-a[i][j-1]==0){
dp[i][j][k]=(dp[i][j][k]+fun(1,k,j,m))%mod;
ans=(ans+dp[i][j][k])%mod;
}
}
}
pre(i);
}
printf("%lld\n",(ans+mod)%mod);
return 0;
}
最后一题:欧拉函数预处理。
考虑
g
c
d
gcd
gcd分别为
[
1
,
n
]
[1,n]
[1,n]的贡献。
对于
g
c
d
(
i
,
j
)
=
k
gcd(i,j)=k
gcd(i,j)=k,显然
i
,
j
i,j
i,j都为
k
k
k的倍数。
且有:对于
[
1
,
n
]
[1,n]
[1,n]求
g
c
d
(
i
,
j
)
=
k
gcd(i,j)=k
gcd(i,j)=k的个数等价于
[
1
,
⌊
n
k
⌋
]
[1,\lfloor\dfrac{n}{k}\rfloor]
[1,⌊kn⌋]求
g
c
d
(
i
,
j
)
=
1
gcd(i,j)=1
gcd(i,j)=1的个数。
考虑到矩阵的对称性,
g
c
d
(
i
,
j
)
=
1
,
i
>
1
gcd(i,j)=1,i>1
gcd(i,j)=1,i>1的个数是
2
φ
(
i
)
2\varphi(i)
2φ(i),
而对角线上只有
g
c
d
(
1
,
1
)
=
1
gcd(1,1)=1
gcd(1,1)=1,所以贡献为
2
∑
i
=
2
⌊
n
k
⌋
φ
(
i
)
+
1
2\sum\limits_{i=2}^{\lfloor\dfrac{n}{k}\rfloor} \varphi(i)+1
2i=2∑⌊kn⌋φ(i)+1
综上枚举一下 k k k的贡献求和即可。
时间复杂度: O ( n ) O(n) O(n)
void Euler(int n){
phi[1]=1;
for(int i=2;i<=n;i++){
if(!vis[i]) p[cnt++]=i,phi[i]=i-1;
for(int j=0;j<cnt&&i*p[j]<=n;j++){
vis[i*p[j]]=1;
if(i%p[j]==0){
phi[i*p[j]]=phi[i]*p[j]%mod;
break;
}
else phi[i*p[j]]=phi[i]*phi[p[j]];
}
}
pre[1]=1;
for(int i=2;i<=n;i++) pre[i]=(pre[i-1]+2*phi[i])%mod;
}
int main(){
int n;scanf("%d",&n);Euler(n);
ll ans=0;
for(int i=1;i<=n;i++)
ans=(ans+1LL*pre[n/i]*i%mod*i%mod)%mod;
printf("%lld\n",ans);