【2019 10th C++】 蓝桥杯决赛
一 \(\checkmark\)
题意
求\(2019<X<Y\),使得\(2019\times 2019 , X\times X , Y\times Y\)构成等差数列,且\(X+Y\)的值尽量小
题解
枚举\(X\)的值,只要\(X\)的值确定了,得到\(Y\),通过开根后平方判断是不是一个整数
Code
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<=n;i++)
int main(){
int X,Y;
int a=2019*2019;
rep(i,2020,100000){
int del=i*i-a;
double y2=i*i+del;
int y=sqrt(y2);
if(y*y==y2){
X=i,Y=y;
break;
}
}
cout<<X+Y<<endl;
}
答案
7020
二 \(\checkmark\)
题意
求解两两不同的素数构成\(2019\)的方案数
题解
\(01\)背包求数字部分和问题
- \(dp[0\sim cnt][0]\)的边界问题
Code
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define ll long long
const int N=2019+10;
ll dp[N][N];
int primes[N],cnt;
bool st[N];
void get()
{
rep(i,2,2019)
{
if(!st[i]) primes[++cnt]=i;
for(int j=1;primes[j]<=2019/i;j++)
{
st[primes[j]*i]=1;
if(i%primes[j]==0) break;
}
}
}
int main()
{
get();
rep(i,0,cnt) dp[i][0]=1;
rep(i,1,cnt) rep(j,1,2019)
{
dp[i][j]=dp[i-1][j];
if(j>=primes[i]) dp[i][j] += dp[i-1][j-primes[i]];
}
cout<<dp[cnt][2019]<<endl;
}
答案
55965365465060
三 \(\checkmark\)
题意
求约数个数为\(100\)的最小整数
题解
从小到大暴力枚举即可
Code
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<=n;i++)
int main()
{
int ans;
for(int i=2;;i++)
{
int d=0;
for(int j=1;j<=i;j++)
{
if(i%j==0) d++;
}
if(d==100)
{
ans=i;
break;
}
}
cout<<ans<<endl;
}
答案
45360
四
题意
将一个\(7\times 7\)的方格沿着边剪开后,右半部分水平翻转后再旋转拼接在左边那块的不规则处,要求拼成的还是一个矩形,求有多少种裁剪方法
题解
Code
五 \(\checkmark\)
题意
有一个\(7\times 7\)的方格。方格左上角顶点坐标为\((0,0)\),右下角坐标为\((7,7)\)。
求满足下列条件的路径条数:
1、起点和终点都是\((0,0)\)
2、路径不自交
3、路径长度不大于\(12\)
4、对于每一个顶点,有上下左右四个方向可以走,但是不能越界。
题解
-
\(dfs\)所有可能的行走方向,在每一个\(dfs\)路径中,标记已经走过的节点,不能重复行走
-
当满足走到\((0,0)\)且\(step\;!=\;0,step\leq 12\),累计答案
Code
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<=n;i++)
const int N=15;
int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
bool st[N][N];
int ans;
void dfs(int x,int y,int step)
{
if(step>12) return;
if(!x && !y && step<=12 && step)
{
++ans;
return;
}
rep(i,0,3)
{
int tx=x+dx[i],ty=y+dy[i];
if(tx >= 0 && tx<7 && ty >= 0 && ty<7)
{
if(st[tx][ty]) continue;
st[tx][ty]=1;
dfs(tx,ty,step+1);
st[tx][ty]=0;
}
}
}
int main()
{
dfs(0,0,0);
cout<<ans<<endl;
}
答案
208
六. 最优包含\(\checkmark\)
题意
给定两个字符串\(A\)和\(B\),保证\(A\)的长度不小于\(B\)的长度,问至少修改\(A\)的多少个字符,可以令\(B\)成为\(A\)的子序列。
题解
子序列不是连续的,所以只要下标的大小关系不变并且包含\(B\)的每一个字母即可
-
\(dp[i][j]\)表示\(A\)的前\(i\)个包含\(B\)的前\(j\)个的操作次数
-
状态转移:\(dp[i][j]=min(dp[i-1][j],dp[i-1][j-1]+(A[i]!=B[j]))\)
注意开始边界的定义\(dp[0][0]=0\)
数据范围
\(1\leq |A|,|B|\leq 1000\)
Code
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<=n;i++)
const int N=1010;
char a[N],b[N];
int dp[N][N];
int n,m;
int main(){
scanf("%s%s",a+1,b+1);
n=strlen(a+1);m=strlen(b+1);
memset(dp,0x3f,sizeof dp);
rep(i,0,n) dp[i][0]=0;
rep(i,1,n) rep(j,1,m){
dp[i][j]=dp[i-1][j];
dp[i][j]=min(dp[i][j],dp[i-1][j-1]+(a[i]!=b[j]));
}
printf("%d\n",dp[n][m]);
}
七. 排列数字
题意
对于一个数列中的某个数,如果这个数比两侧的数都大或比两侧的数都小,我们称这个数为这个数列的一个转折点。
如果一个数列有\(t\)个转折点,我们称这个数列为\(t+1\)调数列。
给定两个正整数\(n,k\)。求在\(1\sim n\)的全排列中,有多少个数列是\(k\)调数列。
数据范围
\(1\leq k\leq n\leq 500\)
题解
-
\(dfs\)求数字的全排列,判断当前是否存在\(k-1\)个转折点,如果是的话累计个数
-
可行性剪枝,如果转折点超过或者加上剩下的所有点数都不够就剪枝
-
每次递归开始的时候累加转折点,所以判断的时候要\(+1\)因为在\(u=n+1\)的时候存储了\(n\)个值的全排列
Code
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<=n;i++)
const int N=1010;
int n,k;
int path[N];
int ans;
bool st[N];
int a[N];
bool check(int x)
{
if(path[x-1]>path[x]&&path[x]<path[x+1]) return 1;
if(path[x-1]<path[x]&&path[x]>path[x+1]) return 1;
return 0;
}
void dfs(int u,int sum)
{
if(u>=4) // 边界问题
{
if(check(u-2)) sum++;
}
if(u == n+1)
{
if(sum== k-1) ans++;
return;
}
if(sum > k-1 || sum+n-u+1 < k-1) return;
rep(i,1,n)
{
if(st[i]) continue;
st[i]=1;
path[u]=i;
dfs(u+1,sum);
st[i]=0;
}
}
int main()
{
scanf("%d%d",&n,&k);
dfs(1,0);
printf("%d\n",ans);
}
八.解谜游戏
题意
题解
Code
九. 第八大奇迹\(\checkmark\)
题意
给定一个长度为\(n\)的序列,初始所有的值都为\(0\),给定\(m\)个操作,操作有两种
-
\((C,x,y)\):将第\(x\)位置上的值修改为\(y\)
-
\((Q,x,y)\):查询问下标为\(x\sim y\)区间中的第\(8\)大数字
数据范围
\(1\leq n,m\leq 10^{5}\)
题解
每次求的只有第\(k\)大的数字,所以线段树的每个节点利用\(vector\)维护当前区间内部的前\(8\)大的数字
-
\(vector\)每次\(push\_back\)都需要动态增加空间,导致\(TLE\),所以在初始化过程中通过\(resize\)将其变为数组
-
每次修改操作后通过引用的方式改变更新节点前八大的数字
数组不能作为函数返回值,所以需要用\(vector\)作为数组进行使用
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,m;
struct node {
int l,r;
vector<int>p;
#define l(u) u*2
#define r(u) u*2+1
}tr[N*4];
void pushup(vector<int>&u,const vector<int>&left,const vector<int>&right){
int i=0,j=0,cnt=0;
rep(t,1,8){
if(left[i]>=right[j]) u[cnt++]=left[i],i++;
else u[cnt++]=right[j],j++;
}
}
void build(int u,int l,int r){
tr[u].l=l;
tr[u].r=r;
tr[u].p.resize(8);
if(l==r){
tr[u].p[0]=0;
return;
}
int mid=l+r>>1;
build(l(u),l,mid);
build(r(u),mid+1,r);
}
void modify(int u,int id,int x){
if(tr[u].l==tr[u].r){
tr[u].p[0]=x;
return;
}
int mid=tr[u].l+tr[u].r>>1;
if(id<=mid) modify(l(u),id,x);
else modify(r(u),id,x);
pushup(tr[u].p,tr[l(u)].p,tr[r(u)].p);
}
vector<int> query(int u,int l,int r){
if(tr[u].l >= l && tr[u].r <= r) return tr[u].p;
int mid=tr[u].l+tr[u].r>>1;
if(r<=mid) return query(l(u),l,r);
if(l>mid) return query(r(u),l,r);
vector<int>ans(8,0);
pushup(ans,query(l(u),l,r),query(r(u),l,r));
return ans;
}
int main(){
scanf("%d%d",&n,&m);
char op[2];int x,y;
build(1,1,n);
while(m--){
scanf("%s%d%d",op,&x,&y);
if(op[0]=='C') modify(1,x,y);
else printf("%d\n",query(1,x,y)[7]);
}
return 0;
}
十.燃烧权杖
题意
数据范围
题解
Code