潍坊一中第二届挑战赛题解

T1 揽月湖

题目链接

本题考察基本表达式与数据类型。

  • int 4个字节 ( − 2 31 , 2 31 − 1 ) (-2^{31},2^{31}-1) (231,2311) ,(-2,147,483,648 到-2,147,483,647)
  • long long 8个字节 − 2 63 , 2 63 − 1 -2^{63},2^{63}-1 263,2631(-9,223,372,036,854,775,808到9,223,372,036,854,775,807)
  • unsigned long long 8个字节 0 , 2 64 0,2^{64} 0,264(0到18,446,744,073,709,551,616)

本题中,用不同的数据类型,得到不同的分数

  • 对于 50 % 50\% 50% 的数据, r ≤ 1 × 1 0 4 r≤1×10^4 r1×104。 最大 3 ∗ 1 0 8 3*10^8 3108 i n t int int范围内,预计得分50
  • 对于 70 % 70\% 70% 的数据, r ≤ 1 × 1 0 9 r≤1×10^9 r1×109。 最大 3 ∗ 1 0 18 3*10^{18} 31018 l o n g   l o n g long \space long long long
    范围内,预计得分70.
  • 对于 100 % 100\% 100% 的数据, r ≤ 2 × 1 0 9 r≤2×10^9 r2×109。最大 1.2 ∗ 1 0 19 1.2*10^{19} 1.21019在$unsigned \space long \space long $范围内。得分100。

参考满分代码:

#include<cstdio>
#include<iostream>
using namespace std;
unsigned long long a;
int main()
{
	cin>>a;
	cout<<a*a*3;
	return 0;
 } 

T2 大写,小写?

题目链接

本题考察字符串的输入及ASCII码转换。

cin只能读入可见字符。用cin只能获得50分。

带空格的字符串读入用getline()

参考代码:

#include<iostream>
#include<string>
#define int long long
using namespace std;

string s;

signed main()
{
	getline(cin, s);
	for(int i=0; i<s.length(); i++)
	{
		if(s[i] >= 'a' && s[i] <= 'z')
		{
			cout << (char)(s[i] - ('a' - 'A'));
		}
		else if(s[i] >= 'A' && s[i] <= 'Z')
		{
			cout << (char)(s[i] - ('A' - 'a'));
		}
		else
		{
			cout << s[i];
		}
	}
	return 0;
}

T3 200天纪念

题目链接

本题考察循环的应用与基本表达式。
注意数据范围用$long \space long $

#include <bits/stdc++.h>
using namespace std;
typedef long long ll; 
int main(){
  ll  N;
  int K;
  cin >> N >> K;
  for (int i = 0; i < K; i++) {
    if (N % 200 == 0) N /= 200;
    else N = N * 1000 + 200;
  }
  cout << N << endl;
  return 0;
}

T4 走方格

题目链接

本题考察分类讨论。
从数据来看,光标的移动只需要向下和向右。为了简化问题,我们把对称的 ( n , m ) ( m , n ) (n,m)(m,n) (n,m)(m,n)归为同一类处理,强制 n < m n<m n<m

如何走最优呢?尽量多利用Fn?
如何最大化利用Fn?

先来看特殊数据 n = 1 ∣ ∣ m = = 1 n=1||m==1 n=1∣∣m==1

1 2 3 4 5 6 7 8

0 1 2 3 3 4 4 5

可以通过打表找规律和计算得出结论

2 + ⌈ ( m − 1 − 2 ) 2 ⌉     m > = 4 2+\lceil\frac {(m-1-2)}{2}\rceil \space \space \space m>=4 2+2(m12)   m>=4

= ⌈ ( m − 1 − 2 + 4 ) 2 ⌉ =\lceil\frac {(m-1-2+4)}{2}\rceil =2(m12+4)

= ⌈ ( m + 1 ) 2 ⌉ =\lceil\frac {(m+1)}{2}\rceil =2(m+1)

= ⌊ m 2 + 1 ⌋ =\lfloor\frac {m}{2}+1\rfloor =2m+1

= m 2 + 1 =\frac {m}{2}+1 =2m+1

n>1的情况 已经规定n<=m

如何尽可能多的运用Fn呢?

先下右走,然后Fn直到下移结束。再按一次右移,就又可以利用Fn了。

$$ f(n,m)=\left{
\begin{aligned}
m,\space n>=m-1 \

(2+(n-2)+1+\frac{(m-n-1)}{2} \space n<m-1,n>=2
\end{aligned}
\right.
$$
( 2 + ( n − 2 ) + 1 + ⌈ ( m − n − 1 ) 2 ⌉ (2+(n-2)+1+\lceil\frac{(m-n-1)}{2}\rceil (2+(n2)+1+2(mn1)

= ⌈ ( 2 n + m − n − 1 ) 2 ⌉ + 1 =\lceil\frac{(2n+m-n-1)}{2}\rceil+1 =2(2n+mn1)+1

= ⌈ ( m + n − 1 ) 2 ⌉ + 1 =\lceil\frac{(m+n-1)}{2}\rceil+1 =2(m+n1)+1

= ⌊ ( m + n ) 2 ⌋ + 1 =\lfloor\frac{(m+n)}{2}\rfloor+1 =2(m+n)+1

= ( m + n ) 2 + 1 =\frac{(m+n)}{2}+1 =2(m+n)+1

参考代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

ll work() {
	ll n,m,ans=0;
	cin>>n>>m; 
	if(n>m)swap(n,m);//n<=m
	if(n==1){
		if(m<4)ans=m-1;
		else ans=m/2+1;	
	} 
	else {
		if(n>=m-1)ans=m ;//n=m-1 n=m 直接下右作为一组。 
		else ans=(n+m)/2+1;
	}
	return ans;

}
int main() {
	int t;
	cin>>t; 
	while(t--) {
		cout<<work()<<"\n";
	}
	return 0;
}

T5寻找三元组

本题考察枚举的优化。

部分分1

枚举ABC,三重循环,预计得分20.

#include<bits/stdc++.h>

using namespace std;
#define int long long
signed main()
{
	int n, ans = 0;
	cin >> n;
	for(int i=1; i<=n; i++)
	{
		for(int j=1; j*i<=n; j++)
		{
			for(int k=1; i*j*k<=n; k++)
			{
				if(i <= j && j <= k && i * j * k <= n)
				{
					ans++;
				}
			}
		}
	}
	cout << ans;
	return 0;
}



优化1,枚举AB,计算C

#include<bits/stdc++.h>
using namespace std;
#define int long long
signed main()
{
	int n, ans = 0;
	cin >> n;
	for(int i=1; i<=n; i++)
	{
		for(int j=i; j*i<=n; j++)
		{
			
			ans+=max(0ll,n/(i*j) -(j-1) ); //a b,c  选出>=b的数 
		}
	}
	cout << ans;
	return 0;
}



优化2

这样发现还是超时。
因为 n < = 1 0 11 n<=10^{11} n<=1011,且 A ≤ B ≤ C , A ∗ B ∗ C ≤ N A \leq B \leq C,A*B*C \leq N ABC,ABCN

这样A的上界还可以缩小,最大为 N 3 \sqrt[3]{N} 3N
b的上界为 N A \sqrt{\frac {N}{A}} AN
总时间复制度约为
∑ a = 1 N 3 ∗ ( N A − A + 1 ) \displaystyle \sum_{a=1}^{\sqrt[3]{N}}*(\sqrt{\frac {N}{A}}-A+1) a=13N (AN A+1)
经过推导,时间复杂度大约为 o ( N 2 3 ) o(N^{\frac {2}{3}}) o(N32)

参考代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
int ans;
signed main()
{
	int n;
	cin >> n;
	for(int i=1; i*i*i<=n; i++)
	{
		for(int j=i; i*j*j<=n; j++)
		{
			ans += n / (i * j) - j + 1;
		}
	}
	cout << ans;
	return 0;
}



T6走方格2

本题考察搜索算法。
我们从 ( 1 , 1 ) (1,1) (1,1) 开始更新每一个点。求最短距离,首选BFS,广度优先搜索。

由于我们要求走到距离其欧式距离为 M \sqrt{M} M 的点,设我们横坐标移动的距离为 x x x,纵坐标移动距离为 y y y

则我们需要寻找这个方程的所有解:
x 2 + y 2 = M x^2+y^2=M x2+y2=M

我们可以只枚举正整数 $x,y $其他负数的解也顺便出来了。

但是枚举 x , y x,y x,y 加上 BFS 时间复杂度为 O ( n 4 ) O(n^4 ) O(n4),需要优化。

  • 优化方法一:可以提前预处理符号要求的变换位置,存储,后面直接使用即可。
  • 优化方法二:枚举 $x $即可找出对应的 $y 或判断无解,所以只枚举 或判断无解,所以只枚举 或判断无解,所以只枚举x$ 即可。

优化后的时间复杂度 O ( n 3 ) O(n^3) O(n3),足已通过本题。

参考代码:

#include<bits/stdc++.h>
using namespace std;
vector<pair<int,int> > e;
int ans[410][410];
bool done[410][410];
signed main() {
	int n,m;
	cin>>n>>m;
	for(int i=-n;i<=n;i++)
		for(int j=-n;j<=n;j++)
			if(i*i+j*j==m) e.push_back({i,j});
	queue<pair<pair<int,int>,int> > q;
	q.push({{1,1},0});
	while(!q.empty()) {
		auto tmp=q.front();q.pop();
		int x=tmp.first.first,y=tmp.first.second,w=tmp.second;
		if(done[x][y]) continue;
		done[x][y]=1,ans[x][y]=w;
		for(auto d:e) {
			int tx=x+d.first,ty=y+d.second;
			if(tx<1||tx>n||ty<1||ty>n) continue;
			if(!done[tx][ty]) q.push({{tx,ty},w+1});
		}
	}
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=n;j++) {
			if(!done[i][j]) cout<<-1<<" ";
			else cout<<ans[i][j]<<" ";
		}
		cout<<endl;
	}
	return 0;
}

T7 数字游戏

题目链接

有的同学用贪心,用最小的和最大的匹配,这是错误的。
例如

1 8 9

9 8 1

最大值并不出现在左右端点,可能出现在中间。因此,正确的方法是将 A i A_i Ai从小到大排序, B i B_i Bi从大到小排序,依次对应,使得最大值最小。

部分分1

每次sort排序,总时间复杂度 o ( n 2 l o g n ) o(n^2log ^n) o(n2logn),预计得分50.

优化

注意到 A i , B i A_i,B_i Ai,Bi的范围比较小。每次对应可以用桶排序,根据鸽巢原理,在n大的情况下,桶内数据可能是多个,可以多组数据一起对应。A桶从小到大,B桶从大到小,匹配求最大值即可。时间复杂度 O ( N ∗ 200 ) O(N*200) O(N200)。预计得分100.
参考代码:

#include<bits/stdc++.h>
using namespace std;
const int N=100009;
typedef long long ll;
int ma[209],mb[209],ta[209],tb[209];
int la,lb,ra,rb;
int n,x,y,ans;
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>x>>y;
		ma[x]++,mb[y]++;
		for(int i=1;i<=200;i++)ta[i]=ma[i],tb[i]=mb[i];
		int k=0,j=200,cnt=0;
		ans=0;
		while(cnt<i){//共匹配i个数
			while(j>0&&!tb[j])j--;//从小到大找bi
			if(j==0)break; 
			while(k<=200&&!ta[k])k++;//从大到小找ai
			if(k>200)break;
			int t=min(ta[k],tb[j]);//每次可以匹配t个数,加快了匹配进度
			cnt+=t;
			ans=max(ans,k+j);
			ta[k]-=t;tb[j]-=t;				
		}
		cout<<ans<<"\n";
	}	
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值