2021蓝桥杯第二次省赛B组题解(全&详细&有PDF)

2021蓝桥杯第二次省赛B组PDF,提取码:1111

没有验证过答案,不保证正确性(但是我觉得都是对的hhh)

试题 A: 求余

1

试题 B: 双阶乘

59375

试题 C: 格点

15698

试题 D: 整数分解

691677274345

记忆化搜索,注意开 l o n g   l o n g long\ long long long

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
#define int long long
int f[2222][6];
int dfs(int x,int k)
{
     
	if( f[x][k]!=-1 )	return f[x][k];
	if( k==0 )	return x==0;
	int ans = 0;
	for(int i=1;i<=x;i++)	ans += dfs(x-i,k-1);
	return f[x][k] = ans;
}
signed main()
{
     
	memset( f,-1,sizeof f );
	cout << dfs(2021,5) << endl;
}

试题 E: 城邦

4046

裸的最小生成树

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int maxn = 2022*2022;
struct p
{
     
	int l,r,w;
	bool operator < ( const p&tmp )	const
	{
     
		return w<tmp.w;
	}
}a[maxn]; int top;
int isok(int l,int r)
{
     
	int ans = 0;
	for(int i=1;i<=4;i++)
	{
     
		if( l%10 != r%10 )	ans += l%10+r%10;
		l /= 10; r /= 10;
	}
	return ans;
}
int fa[maxn];
int find(int x){
      return x==fa[x]?x:fa[x]=find(fa[x]); }
void kur()
{
     
	sort( a+1,a+1+top );
	for(int i=1;i<=2021;i++)	fa[i] = i;
	int k = 0,ans=0;
	for(int i=1;i<=top;i++)
	{
     
		int l = a[i].l, r = a[i].r;
		int fl = find( l ), fr = find( r );
		if( fl==fr )	continue;
		fa[fl] = fr, ans += a[i].w; k++;
	}
	cout << ans ;
}
int main()
{
     
	for(int i=1;i<=2021;i++)
	for(int j=i+1;j<=2021;j++)
	{
     
		++top; a[top].l = i, a[top].r=j;
		a[top].w = isok(i,j); 
	}
	kur();
}

试题 F: 特殊年份

按照题意模拟即可

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
int main()
{
     
	int x,ans = 0;
	for(int i=1;i<=5;i++)
	{
     
		cin >> x;
		int ge = x%10;
		x/=10;
		int shi = x%10;
		x/=10;
		int bai = x%10;
		x/=10;
		int qian = x%10;
		if( qian==shi && ge==bai+1 )	ans++;
	}
	cout << ans;
}

试题 G: 小平方

这里需要特别注意小于 n n n的一半,需要分 n n n的奇偶性讨论一下

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
int main()
{
     
	int ans = 0,n;
	cin >> n;
	for(int i=1;i<n;i++)
	{
     
		int x = i*i;
		if( n%2==0 )
		{
     
			if( x%n<n/2 )	ans++;
		}
		else
		{
     
			if( x%n<=n/2 )	ans++;			
		}
	}
	cout << ans;
}

试题 H: 完全平方数

先欧拉筛得到 1 0 7 10^7 107以内的所有质数

然后对 n n n进行质因子分解,若存在一个质因子只有奇数次幂,那么不是平方数

所以我们添加上一个质因子补齐

又注意到 n < = 1 0 11 n<=10^{11} n<=1011,所以不会有两个或以上的质因子超过 1 0 7 10^7 107

那么最后分解剩下来的 n n n就是一个质数,乘上去即可

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int maxn = 1e7;
int prime[maxn+10],top;
bool vis[maxn+10]; 
void make_prime()
{
     
	for(int i=2;i<=maxn;i++)
	{
     
		if( !vis[i] )	prime[++top] = i;
		for(int j=1;j<=top && prime[j]*i<=maxn;j++)
		{
     
			vis[i*prime[j]] = 1;
			if( i%prime[j]==0 )	break;
		}
	}	
}
long long ans = 1,n;
int main()
{
     
	//对n进行质因子分解 
	//大于等于 
	make_prime();
	cin >> n;
	for(int i=1;i<=top;i++)
	{
     
		int temp = 0;
		while( n%prime[i]==0 )	n /= prime[i], temp++;
		if( temp&1 )	ans = ans*prime[i];
	}
	//这样一来,n只会包含大于1e7的质数,那么最多只有一个
	ans *= n; 
	cout << ans;
}

试题 I: 负载均衡

开一个优先队列,队列内装的是现在在执行的作业

越早结束的作业优先级越大,在堆顶最早被弹出来

每次加入一个 a a a时刻的作业时

我们从优先队列回收掉所有 < = a <=a <=a时候结束的作业即可

这样一直模拟

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int maxn = 1e6+10;
int n,m,v[maxn];
struct p
{
     
	int id,suan,ed;//分别是占用的计算机,占用算力,结束时间
	bool operator < ( const p&tmp )	const
	{
     
		return ed>tmp.ed;//优先级大的是终止时间小的	
	} 
};
priority_queue<p>q;
int main()
{
     
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)	scanf("%d",&v[i] );
	for(int i=1;i<=m;i++)
	{
     
		int a,b,c,d; scanf("%d%d%d%d",&a,&b,&c,&d);
		while( !q.empty() )
		{
     
			p now = q.top();
			if( now.ed>a )	break;
			q.pop();
			v[now.id] += now.suan;
		}
		if( d>v[b] )	printf("-1\n");
		else
		{
     
			p r;
			r.id = b, r.suan = d, r.ed = a+c;
			q.push( r );
			v[b] -= d;
			printf("%d\n",v[b] );
		}
	}
}

试题 J: 国际象棋

容易发现 n n n很小,考虑对 n n n进行状态压缩

也就压缩第 i i i行是否放了马

然后我们考虑第 i i i列的马,只可能和第 i − 1 , i − 2 i-1,i-2 i1,i2列放置的马产生冲突

你可能会说还可能和 i + 1 , i + 2 i+1,i+2 i+1,i+2列产生冲突啊,那是后面的状态了,第 i + 1 i+1 i+1列转移的时候再考虑吧

所以定义 f [ i ] [ j ] [ k ] [ l ] f[i][j][k][l] f[i][j][k][l]表示当前枚举到第 i i i

i i i列放马的状态为 k k k,第 i − 1 i-1 i1列放马的状态为 j j j,一共放了 l l l匹马

然后暴力枚举状态转移即可

提前预处理哪些状态冲突会比较好写

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int mod = 1e9+7;
int n,m,K,b[1009];
int f[102][65][65][22]; 
vector<int>v1[65],v2[65];//v1[i]存的是当第k列状态为i时,第k-1列可行的状态
//v2[i]存的是当第k列状态为i时,第k-2列可行的状态
bool ok[65][65];//ok[i][j]表示当前列为i时,前一列不能是j 
int mx;
int fx[10] = {
     1,1,-1,-1,2,2,-2,-2};
int fy[10] = {
     2,-2,2,-2,1,-1,1,-1};
bool isok(int x,int y,int ex,int ey)
{
     
	for(int i=0;i<=7;i++)
	{
     
		int nx = x+fx[i], ny = y+fy[i];
		if( nx==ex && ny==ey )	return false;//能吃掉 
	}
	return true;
}
void init()
{
     
	for(int i=0;i<mx;i++)
	for(int j=0;j<mx;j++)
	{
     
		int flag1 = 0, flag2 = 0;
		for(int q=1;q<=n;q++)//枚举每一个格子
		{
     
			if( (i&(1<<(q-1)))==0 )	continue;
			for(int w=1;w<=n;w++)
			{
     
				if( (j&(1<<(w-1)))==0 )	continue;				
				if( isok(q,3,w,2)==false )	flag1 = 1;
				if( isok(q,3,w,1)==false )	flag2 = 1;		
			}
		}
		if( flag1==0 )	v1[i].push_back( j );
		else	ok[i][j] = 1;
		if( flag2==0 )	v2[i].push_back( j );
	}
}
int fac[1009];
int quick(int x,int n)
{
     
	int ans = 1;
	for( ; n ; n>>=1,x=1ll*x*x%mod )
		if( n&1 )	ans = 1ll*ans*x%mod;
	return ans;
}
int C(int n,int m)
{
     
	if( m<n )	return 0;
	return 1ll*fac[n]*quick( 1ll*fac[m]*fac[n-m]%mod,mod-2 )%mod;
}
int main()
{
     
	for(int i=1;i<=1000;i++)	b[i] = b[i>>1]+(i&1);
	fac[0] = 1;
	for(int i=1;i<=1000;i++)	fac[i] = 1ll*fac[i-1]*i%mod;
	
	cin >> n >> m >> K;
	if( m==1 )
	{
     
		cout << C(n,K);
		return 0;	
	}
	mx = (1<<n);
	init();//预处理状态 
	for(int i=0;i<mx;i++)
	for(auto v:v1[i] )
		f[2][v][i][b[i]+b[v]] = 1;
		
	for(int i=3;i<=m;i++)//枚举第几列 
	for(int j=0;j<mx;j++)//第i列的状态
	for(auto v:v1[j] )//第i-1列的状态
	for(auto las:v2[j] )//第i-2列的状态 
	{
     
		if( ok[v][las] )	continue;
		for(int k=b[j]+b[v]+b[las];k<=K;k++)//选了多少个马
		{
     
			int nx = k-b[j]; 
			f[i][v][j][k] = ( 1ll*f[i][v][j][k]+f[i-1][las][v][nx] )%mod;
		}
	}	
	int ans = 0;
	for(int i=0;i<mx;i++)
	for(auto u:v1[i] )
		ans = ( 1ll*ans+f[m][u][i][K] )%mod;	
	cout << ans;
}
//n<=6, m<=100, k<=20
//定义f[i][j][k][l]表示枚举到第i列,第i-1列的状态为j,i列的状态为k的方案,且已经放了l个马的方案 

你可能感兴趣的:(2021蓝桥杯第二次省赛B组题解(全&详细&有PDF))