上海市计算机学会竞赛平台2023年7月月赛丙组题目解题报告
上海市计算机学会竞赛平台2023年7月月赛丙组题目解题报告
T1 先行后列(孙誉文)
题意
| 第一列 | 第二列 | 第 m 列 | |||
|---|---|---|---|---|---|
| 第一行 | 1 | 2 | … | … | m | 
| 第二行 | m+1 | m+2 | … | … | 2m | 
| 第三行 | 2m+1 | 2m+2 | … | … | 3m | 
| … | … | … | … | … | … | 
| … | … | … | … | … | … | 
| 第 n 行 | … | … | … | … | nm | 
给我们2个数表示表格的m和n,再给我们1个数(我们设为int变量c),让我们输出这个数所在的行与列,输出先行后列。
分析
1)观察这个表格我们会发现表格含有规律,c%m的结果为c所在的数列,但是我们注意有1个特判:当c%m结果为0时,c所在数列便是第m列。上述逻辑可以用1个if语句来实现。并将所在列赋值给变量b.
2)在得到所在列b的情况下,我们可以借此来推算出c所在的行。随后便可以发现c/m+1的结果就是c所在的行,但是这里也要有1个特判:当c在m列时c/m的结果就已经是c所在的行,这里无需加1。同1这里也可以用if语句来完成。if语句中把所在行赋值给变量a。
3)以上逻辑会发现题目给的n毫无用处,所以可以不定义n变量。只要把读入写为cin>>m>>m>>c;2次读入m,最后的读入会把之前的读入覆盖。(不过保留n也不影响程序)
代码
#include <bits/stdc++.h>
using namespace std;
int m, c, a, b;
int main(){
	cin>>m>>m>>c;
	if (c%m==0) b=m;
	else b=c%m;
	if (b==m) a=c/m;
	else a=c/m+1;
	cout<<a<<" "<<b<<endl;
}
 
T2 兔子序列(陈致臻)
题目描述
序列 fi 的定义如下:
- f1=1
 - f2=a
 - 当 i>2 时,fi=fi−1+fi−2
 
给定一个 k,请问找到 j*,j 满足
fj≤k<fj+1
输入格式
- 第一行:单个整数 a
 - 第二行:单个整数 k
 
输出格式
- 单个整数 j
 
数据范围
- 1≤a≤20
 - 1≤k≤1,000,000,000
 
样例数据
输入: 1
 10
输出: 6
说明: 10 介于 第6个数 与 第7个数 之间
分析
本题为斐波那契数列,不过是把第二个数由1换成了a而已,函数内加不加vh数组都行,这里加了以保证不超时
代码1
#include<bits/stdc++.h>
using namespace std;
int s;
long long a,k,ans,vh[100010];
int f(int s){
	if(vh[s]!=-1) return vh[s];
	if(s==1) return vh[s]=1;
	if(s==2) return vh[s]=a;
	return vh[s]=f(s-1)+f(s-2);
	
}
int main(){
	memset(vh,-1,sizeof(vh));
	cin>>a>>k;
	for(int i=1;i<=100000;i++){
		if(f(i)<=k&&k<f(i+1)){
			ans=i;
			break;
		}
			
	}
	cout<<ans<<endl;
	return 0;
}
 
代码2
#include<bits/stdc++.h>
using namespace std;
int s;
long long a,k,ans,vh[100010];
int f(int s){
	if(s==1) return 1;
	if(s==2) return a;
	return f(s-1)+f(s-2);
	
}
int main(){
	memset(vh,-1,sizeof(vh));
	cin>>a>>k;
	for(int i=1;i<=100000;i++){
		if(f(i)<=k&&k<f(i+1)){
			ans=i;
			break;
		}
			
	}
	cout<<ans<<endl;
	return 0;
}
 
T3 数轴旅行(一)(顾浩骞)
题目描述
在数轴上,一共有  
     
      
       
       
         n 
        
       
      
        n 
       
      
    n 个景点,坐标分别为  
     
      
       
        
        
          x 
         
        
          1 
         
        
       
         , 
        
        
        
          x 
         
        
          2 
         
        
       
         , 
        
        
        
          x 
         
        
          3 
         
        
       
         , 
        
       
         . 
        
       
         . 
        
       
         . 
        
       
         . 
        
       
         , 
        
        
        
          x 
         
        
          n 
         
        
       
      
        x_1 ,x_2,x_3 ,....,x_n 
       
      
    x1,x2,x3,....,xn
 你初始在 
     
      
       
       
         x 
        
       
         = 
        
       
         0 
        
       
      
        x=0 
       
      
    x=0位置,每次你可以往左  
     
      
       
       
         d 
        
       
      
        d 
       
      
    d 个单位或往右  
     
      
       
       
         d 
        
       
      
        d 
       
      
    d 个单位,请问为了访问到每一个景点, 
     
      
       
       
         d 
        
       
      
        d 
       
      
    d 最大可以取到多少?
输入格式
输入共两行:
 第一行,第一个正整数  
     
      
       
       
         n 
        
       
      
        n 
       
      
    n
 第二行, 
     
      
       
       
         n 
        
       
      
        n 
       
      
    n 个整数  
     
      
       
        
        
          x 
         
        
          1 
         
        
       
         , 
        
        
        
          x 
         
        
          2 
         
        
       
         , 
        
        
        
          x 
         
        
          3 
         
        
       
         , 
        
       
         . 
        
       
         . 
        
       
         . 
        
       
         . 
        
       
         , 
        
        
        
          x 
         
        
          n 
         
        
       
      
        x_1 ,x_2 ,x_3 ,....,x_n 
       
      
    x1,x2,x3,....,xn
输出格式
 输出一行,表示答案
数据范围
 对于  
     
      
       
       
         30 
        
       
         % 
        
       
      
        30\% 
       
      
    30% 的数据, 
     
      
       
       
         1 
        
       
         ≤ 
        
       
         n 
        
       
         ≤ 
        
       
         10 
        
       
      
        1≤n≤10 
       
      
    1≤n≤10
 对于  
     
      
       
       
         60 
        
       
         % 
        
       
      
        60\% 
       
      
    60% 的数据, 
     
      
       
       
         1 
        
       
         ≤ 
        
       
         n 
        
       
         ≤ 
        
       
         1 
        
        
        
          0 
         
        
          3 
         
        
       
      
        1≤n≤10^3 
       
      
    1≤n≤103
对于 100 % 100\% 100%的数据, 1 ≤ n ≤ 1 0 5 , − 1 0 9 ≤ x 1 , x 2 . . . x n ≤ 1 0 9 1≤n≤10^5,-10^9≤x_1,x_2...x_n≤10^9 1≤n≤105,−109≤x1,x2...xn≤109
样例数据
 输入:
2
-4 4
 
输出:
4
 
输入:
3
-2 4 10
 
输出:
2
 
分析:本题就相当于求 n n n个数的最大公约数,知道三个的最大公约数是gcd(gcd(x,y),z),那么以此类推,就类似于递推,但是要处理 n = 1 n=1 n=1的情况,可以给 n + 1 n+1 n+1,也就是 n = 2 n=2 n=2,赋一个值,为 n = 1 n=1 n=1的倍数,当 n > 1 n>1 n>1,这个 n + 1 n+1 n+1用不到,当 n = 1 n=1 n=1,用 a 1 , a 2 a_1,a_2 a1,a2求最小公倍数,自然为 a 1 a_1 a1
代码:100分
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,a[100010],ans;
int gcd(int x,int y){return y?gcd(y,x%y):x;}//求最大公约数
signed main(){
	cin>>n;
	for(int i=1;i<=n;++i){cin>>a[i];a[i]=abs(a[i]);}
	a[n+1]=a[1]*2;//刚讲的特判
	ans=gcd(a[1],a[2]);
	for(int i=3;i<=n;++i) ans=gcd(ans,a[i]);//n<3时,这个循环不会执行,但是n=2时上面已经求了最大公约数,但n=1时没第二个数与他求,所以才加了个特判
	cout<< ans << endl;
    return 0;
}
 
T4 模糊匹配(二)(杨晓赫)
内存限制: 256 Mb时间限制: 1000 ms
题目描述
有两个仅包含大写英文字母的字符串 S,T,且字符串 T 是 S 的一个子串。
但由于字符串 S 字迹模糊不清,其某些位置上的字符没有办法进行辨认,这些模糊的位置,用 ? 代替,我们将这个字符串称为S 。
现给定字符串 S ,T,请你求出,满足条件的所有可能的原字符串 S 中,字典序最小的一个。
输入格式
输入共两行:
第一行,一个字符串表示 S
第二行,一个字符串表示 T
输出格式
输出共一行,一个字符串表示答案
数据范围
设 ∣S∣,∣T∣ 分别为字符串 S,T 的长度
对于 30%的数据1≤∣T∣≤∣S∣≤10
对于 60%的数据,1≤∣T∣≤∣S∣≤10^2
对于 100%的数据,1≤∣T∣≤∣S∣≤10^4
数据保证存在字符串 S 满足条件
为保证字符串字典序最小,所有问号应尽可能填’A’,所以若T对应的S上部分没有’?‘或问号对应的是’A’,则应优先填它。
 若无满足条件的,为保证字典序小,应从后往前找,尽可能让’?‘在前,方便填’A’。
#include <bits/stdc++.h>
using namespace std;
string s,t;
int main(){
	cin>>s>>t;
	bool f=true,fl=true;
	int l=s.size(),l1=t.size();
	for(int i=l-1;i>=0;--i){
		int z=i,m=l1-1;
		while(s[z]==t[m]||(t[m]=='A'&&s[z]=='?')){
			if(m==0){
				fl=false;
				break;
			}
			z--;
			m--;		
		}
	}
	if(fl){
	for(int i=l-1;i>=0;--i){
		int k=i,y=l1-1;
		while(s[k]==t[y]||s[k]=='?'){
			if(y==0){
				f=false;
				for(int j=i;j>=i-l1+1;--j){
					if(s[j]=='?'){
						s[j]=t[l1-1-i+j];
					}
				}
				break;
			}
			k--;
			y--;		
		}
		if(f==false)
			break;
	}
}
	for(int i=0;i<=l-1;++i){
		if(s[i]=='?'){
			cout<<"A";
			continue;
		}
		cout<<s[i];
	}
	return 0;
}
 
T5 排列排序(高国皓)
内存限制: 256 Mb时间限制: 1000 ms
题目描述
如果一个整数序列 a 1 , a 2 , … , a n a_1,a_2 ,…,a_n a1,a2,…,an 的每个数字都在 1 到 n 之间,且没有两个数字相等,则称这个序列为全排列。例如1,3,2 以及 4,3,2,1 都是全排列。
我们将所有的全排列排序,定义全排列 a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,…,an 与 b 1 , b 2 , … , b m b_1,b_2,…,b_m b1,b2,…,bm 的排序先后关系如下:
如果 n < m n<m n<m,则 a 序列更靠前
如果 n > m n>m n>m,则 b 序列更靠前
如果 n = m n=m n=m,则以字典序规则比较 a 序列与 b 序列,字典序更小的序列更靠前。
根据上述定义,可以得到
第 1 个全排列是 1
第 2 个全排列是 1 2
第 3 个全排列是 2 1
第 4 个全排列是 1 2 3
给定 k k k,请输出第 k k k 个全排列。
输入格式
单个整数:表示 k k k
输出格式
单独一行:表示第 k k k 个全排列
数据范围
30% 的数据 1 ≤ k ≤ 1000 1≤k≤1000 1≤k≤1000
60% 的数据 1 ≤ k ≤ 1 , 000 , 000 1≤k≤1,000,000 1≤k≤1,000,000
100% 的数据 1 ≤ k ≤ 1 0 15 1≤k≤10^{15} 1≤k≤1015
样例数据
输入:
5
输出:
1 3 2
说明:
根据上述定义可知全排列  
     
      
       
        
        
          a 
         
        
          1 
         
        
       
         , 
        
        
        
          a 
         
        
          2 
         
        
       
         , 
        
       
         . 
        
       
         . 
        
       
         . 
        
       
         , 
        
        
        
          a 
         
        
          n 
         
        
       
      
        a_1,a_2,...,a_n 
       
      
    a1,a2,...,an 中的填数方法有  
     
      
       
        
        
          A 
         
        
          n 
         
        
          n 
         
        
       
      
        A^n_n 
       
      
    Ann( 
     
      
       
       
         n 
        
       
         ! 
        
       
      
        n! 
       
      
    n!) 种,更通俗地说,长度为  
     
      
       
       
         n 
        
       
      
        n 
       
      
    n 的全排列的个数有  
     
      
       
       
         n 
        
       
         ! 
        
       
      
        n! 
       
      
    n! 个。
 那么已知  
     
      
       
       
         k 
        
       
      
        k 
       
      
    k ,那么不断用  
     
      
       
       
         k 
        
       
      
        k 
       
      
    k 减去  
     
      
       
       
         1 
        
       
         ! 
        
       
         , 
        
       
         2 
        
       
         ! 
        
       
         , 
        
       
         3 
        
       
         ! 
        
       
         , 
        
       
         . 
        
       
         . 
        
       
         . 
        
       
         , 
        
       
         c 
        
       
         n 
        
       
         t 
        
       
         ! 
        
       
      
        1!,2!,3!,...,cnt! 
       
      
    1!,2!,3!,...,cnt!直至结果在保证为正的情况下无法再减,那么  
     
      
       
       
         c 
        
       
         n 
        
       
         t 
        
       
      
        cnt 
       
      
    cnt 就是第  
     
      
       
       
         k 
        
       
      
        k 
       
      
    k 个全排列的位数,假设下列代码所用变量已经定义,最终可以求出第  
     
      
       
       
         k 
        
       
      
        k 
       
      
    k 个全排列是位数为  
     
      
       
       
         p 
        
       
         o 
        
       
         s 
        
       
      
        pos 
       
      
    pos 的全排列中的第  
     
      
       
       
         k 
        
       
      
        k 
       
      
    k (两个  
     
      
       
       
         k 
        
       
      
        k 
       
      
    k 不一样)个,其中  
     
      
       
       
         i 
        
       
      
        i 
       
      
    i 存放的是位数(即当前阶乘的参数), 
     
      
       
       
         s 
        
       
      
        s 
       
      
    s 存放阶乘之和, 
     
      
       
       
         l 
        
       
         a 
        
       
         s 
        
       
         t 
        
       
      
        last 
       
      
    last 存放上一个的阶乘(即  
     
      
       
       
         ( 
        
       
         i 
        
       
         − 
        
       
         1 
        
       
         ) 
        
       
         ! 
        
       
      
        (i-1)! 
       
      
    (i−1)! )。
int i=0;
while(++i){
		s+=last*i;
		if(s>k){
			k=k-s+last*i;
			pos=i;
			s=last*i;
			break;
		}
		last*=i;
	}
 
当我们知道它是位数为  
     
      
       
       
         p 
        
       
         o 
        
       
         s 
        
       
      
        pos 
       
      
    pos 的全排列中的第  
     
      
       
       
         k 
        
       
      
        k 
       
      
    k 个时,而  
     
      
       
       
         s 
        
       
      
        s 
       
      
    s 表示  
     
      
       
       
         p 
        
       
         o 
        
       
         s 
        
       
         ! 
        
       
      
        pos! 
       
      
    pos!,我们可以确定第一位,因为第一位有  
     
      
       
       
         p 
        
       
         o 
        
       
         s 
        
       
      
        pos 
       
      
    pos 种可能,而总计有  
     
      
       
       
         p 
        
       
         o 
        
       
         s 
        
       
         ! 
        
       
      
        pos! 
       
      
    pos! 种可能,所以每一种  
     
      
       
       
         p 
        
       
         o 
        
       
         s 
        
       
      
        pos 
       
      
    pos 的可能性都对应着  
     
      
       
       
         ( 
        
       
         p 
        
       
         o 
        
       
         s 
        
       
         − 
        
       
         1 
        
       
         ) 
        
       
         ! 
        
       
      
        (pos-1)! 
       
      
    (pos−1)! 种可能,而它是第  
     
      
       
       
         k 
        
       
      
        k 
       
      
    k 种,所以第一位是第  
     
      
       
       
         k 
        
       
         / 
        
       
         ( 
        
       
         s 
        
       
         / 
        
       
         p 
        
       
         o 
        
       
         s 
        
       
         ) 
        
       
      
        k/(s/pos) 
       
      
    k/(s/pos) (向上取整)个数字。假设第一位是第  
     
      
       
       
         c 
        
       
      
        c 
       
      
    c 个数字,去掉前面的  
     
      
       
       
         c 
        
       
         ∗ 
        
       
         ( 
        
       
         s 
        
       
         / 
        
       
         p 
        
       
         o 
        
       
         s 
        
       
         ) 
        
       
      
        c*(s/pos) 
       
      
    c∗(s/pos) 种可能,剩下的  
     
      
       
       
         k 
        
       
         − 
        
       
         ( 
        
       
         c 
        
       
         − 
        
       
         1 
        
       
         ) 
        
       
         ∗ 
        
       
         ( 
        
       
         s 
        
       
         / 
        
       
         p 
        
       
         o 
        
       
         s 
        
       
         ) 
        
       
      
        k-(c-1)*(s/pos) 
       
      
    k−(c−1)∗(s/pos) 就是它在长度为  
     
      
       
       
         p 
        
       
         o 
        
       
         s 
        
       
         − 
        
       
         1 
        
       
      
        pos-1 
       
      
    pos−1 的全排列中的位置。
 然后设计递归。注意我是第…个数字,而不是…数字。因为当有的数字已经被使用后,不能再次使用,必须继续向后找。
void f(int pos,int s,int k)
 
 
     
      
       
       
         p 
        
       
         o 
        
       
         s 
        
       
      
        pos 
       
      
    pos 表示位数,当其等于1时不再递归; 
     
      
       
       
         s 
        
       
      
        s 
       
      
    s 表示  
     
      
       
       
         p 
        
       
         o 
        
       
         s 
        
       
         ! 
        
       
      
        pos! 
       
      
    pos! 用于计算可能性数量,注意实时的更新; 
     
      
       
       
         k 
        
       
      
        k 
       
      
    k表示的是位次,用于每次输出剩余的首位(即第  
     
      
       
       
         总位数 
        
       
         − 
        
       
         p 
        
       
         o 
        
       
         s 
        
       
         + 
        
       
         1 
        
       
      
        总位数-pos+1 
       
      
    总位数−pos+1 位)。
 至于计算第  
     
      
       
       
         x 
        
       
      
        x 
       
      
    x 个数字,应当使用  
     
      
       
       
         v 
        
       
         h 
        
       
      
        vh 
       
      
    vh 数组进行标记,如果使用过了,即继续查找。
int i=0,cnt=(k+s/pos-1)/(s/pos);
while(cnt--)
	if(vh[++i])++cnt;
cout<<i<<" ";
 
最后说明:因为没有仔细估计题目数据,数组就开到了10000000,具体大家可以算一算 1 0 15 10^{15} 1015 是 1 ! + 2 ! + 3 ! + . . . + n ! 1!+2!+3!+...+n! 1!+2!+3!+...+n!中的 n n n 值,最终算法复杂度为 O ( n 2 ) O(n^2) O(n2)(最坏情况)。
#include <bits/stdc++.h>
#define int long long
using namespace std;
int k,s,last=1,pos,i;
bool vh[10000000];
void f(int pos,int s,int k){
	int i=0,cnt=(k+s/pos-1)/(s/pos);
	int c=cnt;
	while(cnt--)
		if(vh[++i])++cnt;
	cout<<i<<" ";
	vh[i]=1;
	if(pos>1)f(pos-1,s/pos,/*k-(c-1)*(s/pos)*/k%(s/pos)?k%(s/pos):s/pos);//注释中是第二种参数写法
}
signed main(){
	cin>>k;
	while(++i){
		s+=last*i;
		if(s>k){
			k=k-s+last*i;
			pos=i;
			s=last*i;
			break;
		}
		last*=i;
	}
	f(pos,s,k);
	return 0;
}
                        

                
            
        
浙公网安备 33010602011771号