2019第十届国赛真题

A-题目描述:

请找到两个正整数X和Y满足下列条件:
1、2019<X<Y
2、20192、X2、Y2构成等差数列
满足条件的X和Y可能有多种情况,请给出X+Y的值,并且令X+Y尽可能的小。

思路:枚举法,利用等差数列性质(x1+x3)/2=x2求出x3,如何对x3开平方就是y,但是需要判断是否开出来是整数(可以将y再次平方,看数值是否发生变化)


#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
int main(){
	int x1 = 2019*2019;
	for(int x=2020;;x++){
		int x2=x*x;
		int x3=2*x2-x1;
		int y=sqrt(x3);
		if(y*y==x3){//判断是不是开出来的是整数
			printf("%d",x+y);
			break;
		}
	} 
	return 0;
}

答案:2020

B-题目描述:

2019可以被分解成若干个两两不同的素数,请问不同的分解方案有多少种?
注意:分解方案不考虑顺序,如2+2017=2019和2017+2=2019属于同一种方案。

C-题目描述:

锯7X7的木头,然后拼成直角,问有多少种锯法。

D-题目描述:

有一个7X7的方格。方格左上角顶点坐标为(0,0),右下角坐标为(7,7)。
求满足下列条件的路径条数:
1、起点和终点都是(0,0)
2、路径不自交
3、路径长度不大于12
4、对于每一个顶点,有上下左右四个方向可以走,但是不能越界。

思路:搜索剪枝,注意坑点[0,0]->[0,1]->[0,0]和[0,0]->[1,0]->[0,0]要排掉,也就是只走两步的不算

#include<cstdio>
#include<iostream>
using namespace std;
bool vis[100][100];
int ans;
int vir[4][2]={{1,0},{0,1},{0,-1},{-1,0}};
bool in(int x,int y){
	return x>=0&&x<=7&&y>=0&&y<=7;
}
void dfs(int x,int y,int k){
	//printf("x=%d y=%d\n",x,y);
	if(k>12)return;
	if(x==0&&y==0&&vis[x][y]&&k>2){
		ans++;
		return;
	}
	for(int i=0;i<4;i++){
		int tx = vir[i][0]+x;
		int ty = vir[i][1]+y;
		if(in(tx,ty)&&!vis[tx][ty]){
			vis[tx][ty]=true;
			dfs(tx,ty,k+1);
			vis[tx][ty]=false;
		}
	}
	
}
int main(){
	dfs(0,0,0);
	printf("%d\n",ans);
	return 0;
}

E-题目描述:

约数的数量恰好是100的最小正整数。

思路:暴力枚举

#include<iostream>
#include<cstdio>
using namespace std;
int main(){
	for(int i=1;;i++){
		int ans=0;
		for(int j=1;j<=i;j++){
			if(i%j==0)ans++;
		}
		if(ans==100){
			printf("%d\n",i);
			break;
		}
	}
	return 0;
} 

F-题目描述:

题目给定两个字符串S和T,保证S的长度不小于T的长度,问至少修改S的多少个字符,可以令T成为S的子序列。
输入描述:

两行。
第一行是字符串S,第二行是字符串T。
保证S的长度不小于T的长度,S的长度范围在10~1000之间。
输出描述:

答案,一个非负整数。
输入样例:

XBBBBBAC
ACC
输出样例:

2

G-题目描述:

三色三圈的火柴游戏。

H-题目描述:

对于一个数列中的某个数,如果这个数比两侧的数都大或比两侧的数都小,我们称这个数为这个数列的一个转折点。
如果一个数列有t个转折点,我们称这个数列为t+1调数列。
给定两个正整数n,k。求在1~n的全排列中,有多少个数列是k调数列。
输入描述:

两个正整数n,k。
输出描述:

答案,一个整数。
输入样例:

4 2
输出样例:

12

思路:暴力解拿一部分分,全排列+剪枝

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int n,k,ans;
int a[10000000];
bool check(int *a){
	int t=0;
	for(int i=2;i<=n-1;i++){
		if(a[i]>a[i+1]&&a[i]>a[i-1]){
			t++;
		}else if(a[i]<a[i+1]&&a[i]<a[i-1]){
			t++;
		}
		if(t+1>k||(n-i+t+1)<k)return false;//剪枝,转折点过多过少都不行 
	}
	if(t+1==k)return true;
	return false;
	
}
int main(){
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		a[i]=i;
	}
	do{
		if(check(a)){
			ans++;
		}
	}while(next_permutation(a+1,a+1+n));
	cout<<ans<<endl;
	return 0;
} 

I-题目描述:

有一条河,沿河的一侧生活着一个部落。这个一字型的部落有n个据点,从左至右依次编号1~n。
部落的人们有时会在某个据点建立建筑,每个建筑都有各自的价值。一开始,每个据点的都没有建筑,价值都是0。如果在已有建筑的据点建立新的建筑,那么新的建筑会代替旧的建筑(旧的建筑就此消失)。
有两种操作C和Q:
1、C x y,表示在据点x建立一个价值为y的建筑。
2、Q x y,询问在据点x~y之间(包括x,y)的建筑中,价值第八大的建筑的价值是多少。
输入描述:

第一行,两个正整数n和k,表示据点的数量和操作的数量。
接下的k行,每行一个操作。
输出描述:

对于所有的Q操作,输出相应的第八大建筑的价值。
输入样例:

10 14
C 1 5
C 2 4
C 3 7
C 4 6
C 5 5
C 6 1
C 7 8
Q 1 10
C 8 3
C 9 6
C 10 3
Q 1 9
C 6 10
Q 1 10
输出样例:

0
3
4

思路:线段树+排序
线段树维护一段有序区间,查询的时候用一个结果数组将需要查询的区间(节点)映射到结果数组中储存,然后排序输出第八大元素

后来想清楚不需要维护有序的区间(也就是对每个节点排序),只需要维护每个区间的数就可以了,最后映射到数组中再统一排一次序

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int arr[1000000];
int tt[1000]; 
int n,k;
struct node{
	int l,r;
	int a[20];
}tree[10000];
//降序排列 
bool cmp(int a,int b){
	return a>b;
}
void push_up(int node){
	//左孩子维护的区间推给父节点 
	for(int i=tree[node<<1].l;i<=tree[node<<1].r;i++){
		tree[node].a[i]=tree[node<<1].a[i];
		
	}
	//右孩子维护的区间推给父节点 
	for(int i=tree[node<<1|1].l;i<=tree[node<<1|1].r;i++){
		tree[node].a[i]=tree[node<<1|1].a[i];
	}
	//更新后的维护区间进行排序 (这个可以不要,可以大大降低复杂度)
	//sort(tree[node].a+tree[node].l,tree[node].a+tree[node].r+1,cmp);
}
//建树 
void build_tree(int node,int L,int R){
	tree[node].l=L;
	tree[node].r=R;
	if(L==R){
		//初始化数组 
		memset(tree[node].a,0,sizeof(tree[node].a));
		return;
	}
	int mid=(L+R)>>1;
	build_tree(node<<1,L,mid);
	build_tree(node<<1|1,mid+1,R);
	push_up(node);
	return;
}
//更新树 
void update_tree(int node,int L,int R,int x,int num){
	if(L==R){
		//更新叶子节点 
		tree[node].a[x]=num;
		return;
	}
	int mid = (L+R)>>1;
	if(x<=mid){
		update_tree(node<<1,L,mid,x,num);
	}else{
		update_tree(node<<1|1,mid+1,R,x,num);
	}
	//更新父区间 
	push_up(node);
	return;
}
//查询树 
void find_tree(int node,int L,int R,int x,int y){
	//在查询区间之内 
	if(x<=L&&R<=y){
		//将这段维护区间映射到结果数组储存 
		for(int i=tree[node].l;i<=tree[node].r;i++){
			tt[i]=tree[node].a[i];
		}
		return;
	}
	int mid = (L+R)>>1;
	//左子树需要查询 
	if(x<=mid){
		 find_tree(node<<1,L,mid,x,y);
	}
	//右子树需要查询 
	if(y>=mid+1){
		find_tree(node<<1|1,mid+1,R,x,y);
	}
}
//打印树 
void print(){
	for(int i=1;i<=20;i++){
		for(int j=tree[i].l;j<=tree[i].r;j++){
			printf("%d ",tree[i].a[j]);
		}cout<<endl;
	}
}
int main(){
	cin>>n>>k;
	build_tree(1,1,n);
	while(k--){
		char st;
		int x,y;
		cin>>st>>x>>y;
		if(st=='C'){
			update_tree(1,1,n,x,y);
		}else{
			//初始化结果数组 
			memset(tt,0,sizeof(tt));
			find_tree(1,1,n,x,y);
			//对结果数组排序 
			sort(tt+x,tt+y+1,cmp);
			//输出结果数组的第八大元素 
			printf("\n%d\n",tt[x+7]);
		}
	}
	return 0;
}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值