第九届蓝桥杯国赛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,n1]的最大步数(环形) , 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 i1层)有多少满足条件的状态能够递推到 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k],是一个矩阵的形式,所以可以用二维前缀和优化。
第九届蓝桥杯国赛C++B组口胡题解_i++

#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=2knφ(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);