【2018 9th C++】 蓝桥杯决赛
\(1.\) 换零钞
题意
钞票一共有\(100、5、2、1\)四种,现在有\(2\)张\(100\)元的钞票,去换零钱,要换出的零钱中\(2\)元的是\(1\)元的\(10\)倍,剩下的都是\(5\)元面额的
数据范围
无
题解
换的钱张数最少,即\(1\)元的最少,枚举\(1\)元的数目即可,然后得到\(2\)元的数目,最后判断一下剩余的金钱数能不能用\(5\)元的凑出来
Code
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int one=0;
int res=0;
for(int i=1;i<=200;i++)
{
if((200-i*2*10-i)%5==0)
{
one=i;
res=200-(i*2*10+i);
break;
}
}
int ans;
ans=one+10*one;
ans=ans+res/5;
printf("%d\n",ans);
}
\(2.\) 激光样式
题意
\(30\)个灯,相邻的两个不能够同时亮,问一共能够有多少种不同的打开方式,全部关闭也算是一种
数据范围
\(1\leq n\leq 30\)
题解
暴力搜索所有可能的状态,全为\(0\)也算是一种
Code
#include<iostream>
using namespace std;
bool st[35];
int ans=0;
void dfs(int u,int pre){
if(u==31){
ans++;
return;
}
if(pre==0){
dfs(u+1,1);
}
dfs(u+1,0);
}
int main(){
dfs(1,0);
cout<<ans<<endl;
}
\(3.\) 格雷码
题意
代码填空,实现对一个数\(x\)求出其二进制下最右边的一个\(1\),并将其左边的二进制数字进行改变
数据范围
无
题解
\(lowbit\)操作能求出最右边的1,低位补\(0\)后的数字,将其左移一位置,即在后面加一个\(0\),求得的数字即当前最右边的\(1\)左边数字对应的数字为\(1\)的值,将原值与其异或后就可以求得改变后的数字
Code
x ^ (x&(-x)<<1)
\(4.\) 调手表
题意
给定\(n\)进制的手表,和一个\(k\),在任意时刻,有两个操作可以进行,\(+1\)或者\(+k\),手表只能显示\(0\sim n-1\)的数字,
在最优策略下,求出从任意时间到达其他任意时间的操作数的最大值
数据范围
\(0< k < n < 100000\)
题解
因为是\(mod\)循环,可以看做是从\(1\)到\(n-1\)的所有最优方案,最小步数模型,\(bfs\)即可,所有步数的最大值即可。
Code
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<=n;i++)
const int N=1e5+10;
int n,k;
int ans;
int dist[N];
void bfs(){
memset(dist,-1,sizeof dist);
queue< int > q;
q.push(0);
dist[0]=0;
while(q.size()){
int t=q.front();
q.pop();
int tx=(t+1)%n,ty=(t+k)%n;
if(dist[tx]==-1){
dist[tx]=dist[t]+1;
ans=max(ans,dist[tx]);
q.push(tx);
}
if(dist[ty]==-1){
dist[ty]=dist[t]+1;
ans=max(ans,dist[ty]);
q.push(ty);
}
}
}
int main(){
scanf("%d%d",&n,&k);
bfs();
cout<<ans<<endl;
}
\(5.\) 搭积木
题意
输入一个\(n,m\),之后输入\(n\)行,每行\(m\)个字符表示当前是否可以放置,如果为\(.\)则可以放置,如果为\(X\)则不能放置,积木的放置必须满足以下三个规则,
-
当前可以防止积木当且仅当当前位置上一行放置了积木
-
每一行中积木的放置必须是连续的
默认第\(0\)行已经全部放置,求有多少种可能的放置方案,答案\(mod\; 10^{9}+7\)
数据范围
\(1\leq n,m\leq 10^{5}\)
题解
-
状态表示: \(dp[i][l][r]\)在第\(i\)层的\(l\sim r\)区间都放上积木的方案数
-
\(l\sim r\)中能放上积木当且仅当\(i-1\)层中\(l\sim r\)都已经放上了积木,否则就为\(0\)
-
当前行相同的放置,之前的行只要有不同的放置就算是不同的方案
-
-
状态转移:\(dp[i][l][r] = \sum_{j=1}^{l} \sum_{k=r}^{m} dp[i-1][j][k]\)
- 当前的状态计算中不包含都不放的方案,所以答案的初始值需要设置为\(1\)
Code
#include<iostream>
using namespace std;
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,n,a) for(int i=n;i>=a;i--)
#define ll long long
const int mod=1000000007,N=110;
char g[N][N];
ll s[N][N],dp[N][N][N];
int c[N][N];
int n,m;
int ans;
void add_suffix(int i){
rep(j,1,m) rep(k,1,m){
s[j][k]=s[j-1][k]+s[j][k-1]-s[j-1][k-1]+dp[i][j][k];
}
}
ll get_suffix(int x1,int y1,int x2,int y2){
return (s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1])%mod;
}
int main(){
cin>>n>>m;
per(i,n,1) scanf("%s",g[i]+1);
rep(i,1,n)
rep(j,1,m)
c[i][j]=c[i][j-1]+(g[i][j]=='X');
dp[0][1][m]=1;
add_suffix(0);
int res=1;
rep(i,1,n){
rep(j,1,m){
rep(k,j,m){
if(c[i][k]-c[i][j-1]==0){
ll &x=dp[i][j][k];
x=(x+get_suffix(1,k,j,m))%mod;
res=(res+x)%mod;
}
}
}
add_suffix(i);
}
// rep(i,1,n){
// rep(j,1,m){
// cout<<dp[i][j]<<' ';
// }
// cout<<endl;
// }
cout<<(res+mod)%mod<<endl;
}
\(6.\) 矩阵求和
题意
给定一个\(n\),矩阵的每个位置\((i,j)\)元素值等于\(gcd(i,j)\),求\(n\times n\)的矩阵的元素值之和
数据范围
\(1\leq n\leq 10^{7}\)
题解
最大公约数\(d\)的范围为\(1\leq n\)
-
对于\(i,j \in [d,n],gcd(i,j)=d\)的个数
-
\(i,j\in [1,\lfloor \frac{n}{d} \rfloor],gcd(i,j)=1\)的数对的个数是一一对应的
-
满足\(i<j\),且\(gcd(i,j)=1\)的数对个数即\(\sum_{x=2}^{j} \phi(x) + 1\)
-
\(i>j\)时候个数上同\(i<j\)一样,二者都包含了\((1,1)\)容斥掉即可
对于\(i\in [1,i-1]\)中与其互质的数的个数即欧拉函数\(\phi (i)\),
Code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i,a,n) for(int i=a;i<=n;i++)
const int N=1e7+10,mod=1e9+7;
ll phi[N];
ll s[N];
bool st[N];
int primes[N],cnt;
void get_eulers(int n){
phi[1]=1;
for (int i = 2; i <=n ; ++i) {
if(!st[i]) {
primes[cnt++]=i;
phi[i]=i-1;
}
for (int j = 0; primes[j] <=n/i ; ++j) {
st[primes[j]*i]=true;
if(i%primes[j]==0) {
phi[primes[j]*i]=primes[j]*phi[i];
break;
}
phi[primes[j]*i]=phi[i]*(primes[j]-1);
}
}
s[1]=phi[1];
rep(i,2,n) s[i]=(s[i-1]+phi[i]*2)%mod;
}
// void get_eulers(int n){
// rep(i,2,n) phi[i]=0;
// phi[1]=1;
// for(int i=2;i<=n;i++){
// if(!phi[i]){
// for(int j=i;j<=n;j+=i){
// if(!phi[j]) phi[j]=j;
// phi[j]=phi[j]/i*(i-1);
// }
// }
// }
// s[1]=phi[1];
// rep(i,2,n) s[i]=(s[i-1]+phi[i]*2)%mod;
// }
int main(){
int n;
cin>>n;
get_eulers(n);
ll ans=0;
rep(d,1,n) ans=(ans+s[n/d]*d%mod*d)%mod;
cout<<ans<<endl;
}