2020合肥市信息学竞赛试卷(初中组)

本文提供了2020年合肥市初中信息学竞赛的试题解析,涉及数学、数组、数列和树形结构的问题。通过动态规划、几何学和搜索算法解决这些问题,适合OIer和算法爱好者学习。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2020合肥市信息学竞赛试卷(初中组)
1.小C的数学(math)
小C想要成为一名OIer,于是他提前学习数学,为OI做好铺垫。这一天,他的数学老师给了一道题:给定正整数a,以及给定一个区间[b,c],其中b,c均为整数(b,c保证非负)。寻找所有合法的x,满足b≤x≤c,并且a能够整除x,即x除以a的余数为0。
可小C很懒,不想找出来所有的解,他只想知道这样的x有多少个。
输入格式
共一行,依次三个整数a,b,c,如题目所描述。
输出格式
仅一行一个数,表示答案。
输入样例1
2 3 6 
输出样例1
2
样例解释1
x可以为4,6,总共有2个。
输入样例2
3 1 7 
输出样例2
2
样例解释
x可以为3,6,总共有2个。
数据范围
对于40%的数据:0<a≤10^3,0≤b≤c≤10^3;
对于70%的数据:0<a≤10^7,0≤b≤c≤10^7;
对于100%的数据:0<a≤10^9,0≤b≤c≤10^18。

#include <iostream>
#include <cstdio>
using namespace std;
int main()
{
	long long a,b,c,ans;
	cin>>a>>b>>c;
	if(b==0) ans=c/a+1;
	else ans=c/a-(b-1)/a;
	cout<<ans;
    return 0;
}

2.小C的数组(array)
题目描述
小C终于成为一名萌新OIer,最近他在学习数组。小C要练习数组。一次,小C得到了一个长度为n的数组a。现在,对于每一个下标小C想找出比i小且距离i最近的下标j,使得满足aifa;,如果不存在,则j=0。记下标i对应的答案f=j,小C为了确保自己的程序正确,想让你来检查f数可你不能告诉他整个答案,你只需要告诉他f数组所有元素的和即可。
输入格式
共两行,第一行一个正整数n,表示数组长度;第二行n个正整数,第i个表示ai。
输出格式
仅一行一个数,表示f数组所有元素的和。
输入样例1
6
1 1 2 3 2 1
输出样例1
14
样例解释1
f依次为(0,0,2,3,4,5),总和为14。
输入样例2
12
3 3 3 3 2 2 2 2 4 4 1 1
输出样例2
52
样例解释2
f依次为(0,0,0,0,4,4,4,4,8,8,10,10),总和为52。
数据范围
对于40%的数据:n≤1000,1≤ai≤10,且保证数据随机;
对于70%的数据:n≤10^6,1≤ai≤10,且保证数据随机;
对于90%的数据:n≤10^6,1≤ai≤10;
对于100%的数据:n≤10^6,1≤ai≤1000。
随机数据的生成方式如下:对于每一个ai,等概率地从1到10中产生。

#include <iostream>
#include <cstdio>
using namespace std;
const int N=1000005;
int n,a[N];
int main()
{
	scanf("%d",&n);
	long long ans=0;
	for(int i=1,j=0;i<=n;i++){
		scanf("%d",&a[i]);
		if(a[i]!=a[i-1]) j=i-1;
		ans+=j;
	} 
	printf("%lld\n",ans);
    return 0;
}

3.小C的数(number)
题目描述
小C非常喜欢数字。这次,他得到了一个长度为n的正整数数列A,第i项为ai。
现在,小C想要找出来A中最长的子序列B={b1,b2,...,bm},使得对于任意的二元组(i,j),bi和bj满足倍数关系。小C突然不会最长不下降子序列了,于是找到了你来帮他求出最长的子序列的长度。
子序列:对于长度为n的数列A,对于B={b1,b2,…,bm},若b1=ap1,b2=ap2,,bm=apm,则必须要满足1≤p1<p2<…<pm≤n,这样的数列B称为A的子序列。其中子序列B可以为空。
倍数关系:对于两个数a,b,两者成倍数关系,即a能被b整除,或者b能被a整除,两者至少一种成立。
输入格式
共两行,第一行有一个正整数n,表示数列A的长度;第二行有n个正整数,第i个数表示ai。
输出格式
仅一行一个数,表示子序列长度的最大值。
输入样例1
5
1 2 3 8 16
输出样例1
4
样例解释1
最长子序列为{1,2,8,16},长度为4。
输入样例2
10
1 4 6 3 9 11 16 24 42 36
输出样例2
4
样例解释2
最长长度为4,选取方案不唯一,其中一种最长的子序列为{1,6,3,24}。
数据范围


对于所有数据:0<N≤3×10^6,10<ai≤10^7。
如有需要,请使用scanf 读入以及减少使用STL,以降低程序本身带来的常数。

在原序列中选择最长子序列,满足子序列中任意两个元素之间存在倍数关系。
算法:动态规划
状态:f[v]表示V在序列中出现时的最长子序列长度。
状态转移:f[v]=max(f[v*2],f[v*3],f[v*4]...f[v*k])v*k<=maxv
递推的方向:从最大值(maxv)向最小值(1)推
初值:f[maxv]=cnt(maxv)cnt就是maxv出现的次数

//动规 80分解法
#include <iostream>
#include <cstdio>
using namespace std;
const int N=1000005;
int n,f[N],cnt[N];
int main()
{
	scanf("%d",&n);
	int maxv=0,x;
	for(int i=1;i<=n;i++){
		scanf("%d",&x);
		maxv=max(maxv,x);
		cnt[x]++;
	} 
	f[maxv]=cnt[maxv];
	int ans=f[maxv];
	for(int i=maxv-1;i>=1;i--){
		if(cnt[i]==0) continue;
		f[i]=cnt[i];
		for(int k=2;k*i<=maxv;k++){
			f[i]=max(f[i],cnt[i]+f[k*i]);
			ans=max(ans,f[i]);
		}
	}
	printf("%d\n",ans);
    return 0;
}
//埃氏筛 100分解法
#include <iostream>
#include <cstdio>
using namespace std;
const int N=1000005;
int n,f[N],cnt[N];
bool isprimes[N];
int primes[N],pn;
void fastPrimes(int n){//线性筛
	isprimes[0]=isprimes[1]=true;
	for(int i=2;i<=n;i++){
		if(!isprimes[i]) primes[++pn]=i;
		for(int j=1;i*primes[j]<=n;j++){
			isprimes[i*primes[j]]=true;
			if(i%primes[j]==0) break;
		}
	}
}
int main()
{
	scanf("%d",&n);
	int maxv=0,x;
	for(int i=1;i<=n;i++){
		scanf("%d",&x);
		maxv=max(maxv,x);
		cnt[x]++;
	} 
	fastPrimes(maxv);
	f[maxv]=cnt[maxv];
	int ans=f[maxv];
	for(int i=maxv-1;i>=1;i--){
		//if(cnt[i]==0) continue;
		f[i]=cnt[i];
		for(int j=1;j<=pn&&primes[j]*i<=maxv;j++){
			f[i]=max(f[i],cnt[i]+f[primes[j]*i]);
			ans=max(ans,f[i]);
		}
	}
	printf("%d\n",ans);
    return 0;
}

4.小C的树(tree)
题目描述
定义:树是一个有n个点n-1条边的无向连通图,任意两个点间恰有一条简单路径。最未端仅有一条边相连的节点称为叶子。小C家有一个后院,院子的中央种有一棵树。小C喜欢在雨后初睛时到院子里观察。
这棵参天大树可以抽象成一棵n个节点的树,节点从1到n编号,树的根在1号节点。第i个节点有一个正整数a;表示该处的美感值。
小C尤其喜欢观察蚂蚁。这天,小C捉来了m只蚂蚁。他想挑选树上的m个节点,每个节点放上一只蚂蚁。小C知道蚂蚁在树上时,会一直朝着根的方向移动,中间会经过一个个节点,最后到达根节点,之后,蚂蚁便到达了地面。
热爱自然的小C想知道这m只蚂蚁经过的节点的美感值和的最大值是多少,于是请你来帮帮忙。注意经过的节点包括初始节点,且多次访问的节点仅算入一次。
输入格式
共三行,第一行有两个正整数n,m,表示树的大小和选取的节点数;第二行有n个非负整数,第i个数表示ai;第三行有n-1个正整数,第i个数f,表示节点i+1的父亲节点。保证0<fi≤i。
输出格式
仅一行一个数,表示美感值和的最大值。
输入样例1
5 2
1 2 3 1 3
1 1 2 2
输出样例1
9
样例解释1
这棵树的形态如下图:


选择{3,5}两个节点,则经过的节点集合为{1,2,3,5}。


红色数字为每个节点的美感值,故美感值和为1+2+3+3=9。可以证明这个值是最大值。
输入样例2
10 3
3 4 2 2 3 1 3 3 5 4
1 2 1 1 3 3 2 4 6
输出样例2
24
样例解释2
选取{4,5,10}三个节点,得到的美感值和为24,方案可能不唯一。
数据范围


对于所有数据:0<m≤n≤5×10^6,0≤ai≤5。

由于每个节点仅能被访问一次,每个节点仅能属于一条链路。
链路:从叶子节点到根节点的一条路径。
问题转换:
给定一棵树,要求分解成若干条互不相交的链路,求在这么链路中选择m条链路的和的最大值。(贪心算法)
解题框架
1.将树分解为多条链  a.dfs
2.对链从小到大排序  a.sort
3.取前m个链,求和  a.遍历

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=5000010;
int h[N],w[N],e[2*N],ne[2*N],idx;
int n,m,sum[N],son[N],s[N],k;
bool st[N];
void add(int a,int b){//链式前向星
	e[idx]=b;ne[idx]=h[a];h[a]=idx++;
}
//树链剖分
void dfs(int u,int father){//u表示当前节点father父节点
	sum[u]=w[u];//sum保存以u为根节点的重链路的和
	for(int i=h[u];~i;i=ne[i]){//遍历所有的子节点
		int j=e[i];
		if(j==father) continue;
		dfs(j,u);
		if(sum[son[u]]<sum[j]){
			sum[u]=sum[j]+w[u];
			st[son[u]]=false;
			son[u]=j;st[j]=true;
		}
	}
}
int main()
{
	scanf("%d %d",&n,&m);
	memset(h,-1,sizeof(h));
	for(int i=1;i<=n;i++) scanf("%d",&w[i]);
	for(int i=2;i<=n;i++){
		int j;scanf("%d",&j);
		add(i,j);add(j,i);
	}
	dfs(1,-1);
	//将所有的轻节点和全部放到s数组
	for(int i=1;i<=n;i++){
		if(st[i]==false) s[++k]=sum[i];
	}
	sort(s+1,s+k+1,greater<int>());//从大到小排序
	int ans=0;
	for(int i=1;i<=m&&i<=k;i++) ans+=s[i];
	printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值