杭州师范大学第十二届程序设计竞赛之旅——杨子曰

本文记录了一场ACM竞赛的经历,共解答5题,涉及字符串处理、排序、数学问题等,尤其详细解析了B题的暴力解法及K题的精度处理技巧。

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

第二次ACM,感觉被虐了
尽管有大佬:慕容宝宝chhokmah的带领,还是感觉被虐了
一共13道,A了5道签到题,其中一道浪费了两个小时,然后就封榜了,然后就没有然后了………………


比赛题目一览:
A:Little Sub and Applese
B:Little Sub and Triples
C:Little Sub and Sequence
D:Little Sub and Balloons
E:Little Sub and Traveling
F:Little Sub and Game
G:Little Sub and Piggybank
H:Little Sub and Counting
I:Little Sub and Enigma
J:Little Sub and Apples
K:Little Sub and Triangles
L:Little Sub and AA
M:Little Sub and Johann


翻开试题一道这样的题映入眼帘:A
吼哈哈,这也太简单了吧,就是把句子的最后一个句号改成叹号
chhokmah随手打下了代码,WA……
原因:scanf不读空格
chhokmah又随手打下了代码,WA……
原因:gets不能用EOF
弃赛的念头涌上心头,然后破罐子破摔地乱改乱交的几次,听取WA声一片……
最终慕容宝宝逆转乾坤,AC!!!
额,A了一道签到题,我们的罚时已经多得不得了了
A题:AC代码:

#include<cstring>
#include<cstdio>
#include<iostream>

char s[1000005];

int main(){
	while(gets(s)){
		if (s[strlen(s)-1]=='.') s[strlen(s)-1]='!';
		puts(s); 
	}
	return 0;
}

然后看一下rank,发现D题J题两条原谅色,然后开始我想D,慕容宝宝想J
5min后……
杨子曰:D题就是给你n个数,问你n个数中有几个不同的数,如此简单
慕容宝宝曰:J题就是给你n,k,t,求max(0,n-k*t),如此简单

只看chhokmah劈里啪啦,double AC!!开心啊啊!!!

D题AC代码:

#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<map>
using namespace std;

int n,ans=0;
map<int,int> mp;

int main(){
	scanf("%d",&n);
	for (int i=1;i<=n;i++){
		int x;
		scanf("%d",&x);
		if (!mp[x]) ans++,mp[x]=1; 
	}
	cout<<ans;
	return 0;
}

J题AC代码:

#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<map>
using namespace std;

int n,k,t;

int main(){
	scanf("%d%d%d",&n,&k,&t);
	long long ans=max((long long)0,(long long)n-(long long)k*t);
	cout<<ans;
	return 0;
}

然后再看下rank,哦,B题H题是新大陆啊
先看下B题:什么还有自然对数的吗??仔细一看好吧条件4其实就是 A x + A y &gt; A z A_x+A_y&gt;A_z Ax+Ay>Az,哦那不就是可以构成三角形的意思吗?所以说题目就是:给你一个序列,对于每个询问l,r,问该序列[l,r]区间内有没有三个数满足三角形三边关系

左想右想,依然没有思路,所以我们 毫无进取精神 知难而退,就果断放弃了


比赛结束后,看下题解,发现B题就是个暴力题,为神马呢?暴力的想法是对区间排序后,暴力查看相邻的三个数能不能满足条件,如果一直不满足,也就是a[i]+a[i+1]<=a[i+2]也就是说这个序列的增长速度,至少是斐波那契数列的增长速度,打个表后发现,斐波那契数列第56项就炸int了,所以当区间长度大于60时直接输出YES,其他情况就直接暴力呗,So,最终复杂度O(q*60 log 60)
B题AC代码:

#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<map>
using namespace std;

const int maxn=200005;

int n,q;
long long a[maxn],x[maxn];

int main(){
	scanf("%d%d",&n,&q);
	for (int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
	}
	while(q--){
		int l,r;
		scanf("%d%d",&l,&r);
		if (r<l+2 || l<1|| r>n ) printf("NO\n");
		else if (r-l>=60) printf("YES\n");
		else{
			for (int i=l;i<=r;i++){
				x[i]=a[i];
			}
			sort(x+l,x+r+1);
			int flag=0;
			for (int i=l;i<=r-2;i++){
				if (1LL*(x[i]+x[i+1])>x[i+2]){
					printf("YES\n");
					flag=1;
					break;
				}
			}
			if (!flag) printf("NO\n");
		}
	}
	return 0;
}

回到比赛现场
放弃了B题后果断来到了H题,题目很好理解,就是给你一个序列A,对于每一项 A i A_i Ai输出有多少个 A j A_j Aj满足 A i A j &gt; A j A i A_i^{ A_j}&gt;A_j^{ A_i} AiAj>AjAi,慕容宝宝认为对于 a b a^b ab b a b^a ba这样的两个数底数大的数大,然后我随手举了个反例 2 100 2^{100} 2100 10 0 2 100^2 1002,慕容宝宝:……

我提出:指数大的数大,慕容宝宝随手一个反例 2 3 2^3 23 3 2 3^2 32,我:……
欧,如果把我们两个的想法融合一下会发现如下结论:
对于1,答案永远是0
对于2,3是特例特殊判断
而后面的情况则是指数的的数大
所以排序后搞一搞,完事,chhokmah随手打完了代码,AC!

H题AC代码:

#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<map>
using namespace std;

const int maxn=100005;

struct S{
	int x,id;
}a[maxn];

int ans[maxn];

int n;

map<int,int> mp;

int cmp(S a,S b){
	return a.x<b.x;
}

int main(){
	scanf("%d",&n);
	for (int i=1;i<=n;i++){
		scanf("%d",&a[i].x);
		mp[a[i].x]++;
		a[i].id=i;
	}
	sort(a+1,a+n+1,cmp);
	int sum=0;
	for (int i=1;i<=n;i++){
		if (a[i].x==1) ans[a[i].id]=0;
		else if (a[i].x!=a[i-1].x){
			sum+=mp[a[i].x];
			ans[a[i].id]=n-sum;
		}
		else{
			ans[a[i].id]=ans[a[i-1].id];
		}
	}
	for (int i=1;i<=n;i++){
		if (a[i].x==2) ans[a[i].id]-=mp[3]+mp[4];
		else if (a[i].x==3) ans[a[i].id]+=mp[2];
	}
	for (int i=1;i<=n;i++){
		cout<<ans[i]<<' ';
	}
	return 0;
}

至此,我们A了4道题了,然而时间只过去了1个小时,粗略一算,我们3个半小时就可以AK了,哈哈哈哈,想法是美好的,现实是残酷的:

于是我们又来到了K题
就是说给你n个点,q个询问,对于每个询问l,r,求有多少个由这些点构成的三角形面积在[l,r]内。

嗯,先算算n个点最多可以构成多少给三角形:欧,2573000个,蛮好的,不仅存得下,而且排序也不会炸,然后就是看[l,r]区间内有多少个数了,那不就是二分吗!!!!!!

chhokmah问:三角形的面积怎么算?
杨子曰:看我的博客:海伦公式的证明——杨子曰数学
chhokmah: 欧,懂了懂了

然后开始打代码,不错不错,样例过了,交,WA
(然后中间又破罐子破摔了几次)
100min过去了
就在放弃的那一刹那,我突然试出了一组错误样例:

5 1
2 2
1 1
0 0
-1 -1
-2 -2
0 0

把面积输出来看看,居然有nan,显然是对负数开根了,可恶的精度误差
加个abs
又wa了,显然又是精度误差
然后又对区间左边开大0.1,右边开大0.1,AC!!!!!!!
终于,这道题花了我们两个小时,终于AC了,不久就封榜了

K题AC代码:

#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;

const int maxn=255;

int n,q;
double x[maxn],y[maxn],s[8000005];

double sqr(double x){
	return x*x;
}

double dis(int i,int j){
	return sqrt(sqr(x[i]-x[j])+sqr(y[i]-y[j]));
}

double sqrtt(double x){
	return sqrt(abs(x));
}

double calc(double a,double b,double c){
	return sqrtt((a+b+c)/2.0)*sqrtt((-a+b+c)/2.0)*sqrtt((a-b+c)/2.0)*sqrtt((a+b-c)/2.0);
}

int main(){
	scanf("%d%d",&n,&q);
	for (int i=1;i<=n;i++){
		scanf("%lf%lf",&x[i],&y[i]);
	}
	int num=0;
	for (int i=1;i<=n-2;i++){
		for (int j=i+1;j<=n-1;j++){
			for (int k=j+1;k<=n;k++){
				s[++num]=calc(dis(i,j),dis(i,k),dis(j,k));
			}
		}
	}
	sort(s+1,s+num+1);
	while (q--){
		double l,r;
		scanf("%lf%lf",&l,&r);
		int d1=lower_bound(s+1,s+num+1,l-0.01)-s;
		int d2=upper_bound(s+1,s+num+1,r+0.01)-s;
		printf("%d\n",d2-d1);
	}
	return 0;
}

再看下排名,我们又到了I题
看下样例,哈哈哈哈哈哈哈,如此简单,不就是上下对应吗!为什么只有如此少的人A
随手一打,WA
从上面到下面一一对应,从下面到上面也要一一对应
WA
难道说,相同的字母不能对应?
WA
难道说,一个字母变成另一个字母,还可以继续变,所以要判环?
WA
……
我们的罚时飞速上升,于是比赛结束了我们也没有A掉这道题,一定是题目没看懂(←论英语的重要性)


看了题解,我惊了,这道题其实只有3个要求:

  1. 从上到下要一一对应
  2. 从下到上要一一对应
  3. 如果25个字母已经有了一一对应的关系,那么可以推理出第26个字母的对应关系,也要输出!!!!!

这…我无言以对

I题AC代码:

#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<map>
using namespace std;

char s1[1000005],s2[1000005];
int d1[30],d2[30];

int main(){
	memset(d1,0,sizeof(d1));
	memset(d2,0,sizeof(d2));
	gets(s1);
	gets(s2);
	int n=strlen(s1)-1;
	for (int i=0;i<=n;i++){
		int k1=s1[i]-'a'+1;
		int k2=s2[i]-'a'+1;
		if (d1[k1] && d1[k1]!=k2){
			cout<<"Impossible";
			return 0;
		} 
		if (!d1[k1]) d1[k1]=k2;
		if (d2[k2] && d2[k2]!=k1){
			cout<<"Impossible";
			return 0;
		}
		if (!d2[k2]) d2[k2]=k1;
	}
	int num=0;
	for (int i=1;i<=26;i++){
		if (!d1[i]) num++;
	}
	if(num==1){
		int k1,k2;
		for (int i=1;i<=26;i++){
			if (!d1[i]) k1=i;
			if (!d2[i]) k2=i;
		}
		d1[k1]=k2;
		d2[k2]=k1;
	} 
	for (int i=1;i<=26;i++){
	//	cout<<i<<' '<<d1[i]<<endl;
		if (!d1[i]) continue;
		printf("%c->%c\n",i+'a'-1,d1[i]+'a'-1);
	}
	return 0;
}


于是比赛结束了,5道AC,还可以吧,但 I题把我们虐待太惨了…
88


比赛其他的题目将持续更新……

于HG机房

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值