【2021.09.25】(团队赛)ICPC2021东亚洲区网络预选赛第二场(The 2021 ICPC Asia Regionals Online Contest II)
比赛相关信息
比赛信息:
比赛名称: The 2021 ICPC Asia Regionals Online Contest (II) PTA, September 25th, 2021
组织方: 北京大学 && ICPC中国组委会
比赛形式: 线上
赛制: ACM / 团体
比赛地址: 网址
队伍信息:
队名: 高等数学半价出售
成员: WIDA、杰哥、Hamine
比赛过程回顾:
A | B | C | D | E | F | G | H | I | J | K | L | M | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
提交次数 | 0 | 0 | 0 | 0 | 0 | 0 | 2 | 0 | 0 | 1 | 0 | 0 | 4 |
首次提交时间 | 00:49:13 | 03:39:17 | 01:17:31 | ||||||||||
首A时间 | 00:50:27 | 03:39:17 | 02:52:50 |
部分题解与小结
Problem G. Limit
tag:
⇔复杂数论(泰勒展开/洛必达法则)
题意:
求解 \(\lim_{x \to 0}\frac{\sum_{i=1}^{n}a_i*ln(i+b_i*x)}{x^t}\) ,其中,\(1\leq n\leq 100000, −100 \leq a_i, b_i \leq 100, 0 \leq t \leq 5\) 。
思考过程:
题目所给定的 \(t\) 很小,故对 \(t\) 的取值进行分类讨论,然后使用洛必达方式进行多次求导即可推导出规律。
正解:
出题官方将此题定义为中简题。
我们团队的思考过程实际上就是洛必达法则的推导过程,故最正确最直接的解法即直接代入使用洛必达法则。
AC代码:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int MAX5=100005;
LL ans1,ans2,ans3,ans4,ans5,n,t,a[MAX5],b[MAX5];
int main(){
scanf("%lld%lld",&n,&t);
for(int i=1;i<=n;i++) scanf("%lld%lld",&a[i],&b[i]);
if(t==0) cout<<"0";
else{
for(int i=1;i<=n;i++){
ans1+=a[i]*b[i];
ans2+=a[i]*b[i]*b[i];
ans3+=a[i]*b[i]*b[i]*b[i];
ans4+=a[i]*b[i]*b[i]*b[i]*b[i];
ans5+=a[i]*b[i]*b[i]*b[i]*b[i]*b[i];
}
if(t==1) printf("%lld",ans1);
else if(t==2 && ans1==0){
if(ans2%2==0){
printf("%lld",-ans2/2);
}
else printf("%lld/2",-ans2);
}
else if(t==3 && ans1==0 && ans2==0){
if(ans3%3==0){
printf("%lld",ans3/3);
}
else printf("%lld/3",ans3);
}
else if(t==4 && ans1==0 && ans2==0 && ans3==0){
if(ans4%4==0){
printf("%lld",-ans4/4);
}else if(ans4%2==0){
printf("%lld/2",-ans4/2);
}
else printf("%lld/4",-ans4);
}
else if(t==5 && ans1==0 && ans2==0 && ans3==0 && ans4==0){
if(ans5%5==0){
printf("%lld",ans5/5);
}
else printf("%lld/5",ans5);
}
else cout<<"infinity";
}
return 0;
}
错误次数:1次
原因:测试程序可行性结束后内容没删完,编译失败一次。
Problem M. Addition
tag:
⇔进制、⇔进位
题意:
给定 \(sgn_n,a_n,b_n\) 数组,要求计算出满足 \(\sum_{i=0}^{n-1}sgn_i*(a_i+b_i)*2^i=\sum_{i=0}^{n-1}sgn_i*c_i*2^i\) 的 \(C_i\) 数组,其中,\(32\leq n\leq 60\) 。
(赛后补充)其实本题的题意可以抽象地更加具体一些:定义一种新的二进制,其每一位都有符号,代表这一位的正负。对于给定的 \(a,b\) 以及符号数组 \(sgn_n\) ,求解 \(a+b\) 的值。
思考过程:
刚拿到这道题的时候,我们并没有意识到题目中所给定的 \(n\) 的范围的意义,误以为 \(a,b\) 的二进制长度为多少,答案的二进制长度就为多少,于是想当然的将这道题理解成了进制转换题。交了一发WA才意识到题目没这么简单。
(附)hack数据:
-1 -1 1 -1
1 1 0 0
1 1 0 0
随后,我们意识到这道题涉及到的是进位的运算,且需要考虑正数进位和负数进位两种情况。而为了解决正负问题,我们引入了两个变量分别表示正进位和负进位(参见AC代码2)。
正解:
出题官方将此题定义为简单题。
赛后重新推导,我们简化了进位问题,直接使用一个变量 \(add\) 来表示进位。但需要加入一个符号的判断。
- 第 \(i\) 位的计算结果即为:\(sum=a+b+add\) (参见AC代码1)。
- 若 \(sum\) 的符号和这一位给定的符号相同,\(sum/2\) 即为这一位的最终答案,\(sum\%2\) 即为这一位的进位。
- 若不相同,则直接输出 \(1\) 。
AC代码1:
#include<bits/stdc++.h>
using namespace std;
const int MAX4=1050;
//===============================================================
LL n,a[MAX4],b[MAX4],c[MAX4],sgn[MAX4],add,sum;
//===============================================================
int main(){
ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++) cin>>sgn[i];
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i];
for(int i=1;i<=n;i++){
sum=sgn[i]*(a[i]+b[i])+add;
if((sum>0 && sgn[i]<0) || (sum<0 && sgn[i]>0)){//若符号不同
c[i]=1;
}else{
c[i]=sum;
add=c[i]/2;//进位
c[i]=abs(c[i])%2;
}
}
for(int i=1;i<n;i++) cout<<c[i]<<" ";
cout<<c[n];
return 0;
}
AC代码2:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int maxn=1050;
#define ms(a,b) memset(a,b,sizeof (a))
LL n,num1=0,num2=0,a[maxn],b[maxn],sgn[maxn],pp[maxn];
int main(){
scanf("%lld",&n);
for(int i=1;i<=n;i++) scanf("%lld",&sgn[i]);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(int i=1;i<=n;i++) scanf("%lld",&b[i]);
ms(pp,0);
for(int i=1;i<=n;i++){
if (sgn[i]==1){
if (a[i]+b[i]+num1-num2<0){
pp[i]=1;
}else{
pp[i]=(a[i]+b[i]+num1-num2)%2;
num1=(a[i]+b[i]+num1-num2)/2;
num2=0;
}
}else{
if (a[i]+b[i]+num2-num1<0){
pp[i]=1;
}else{
pp[i]=(a[i]+b[i]+num2-num1)%2;
num2=(a[i]+b[i]+num2-num1)/2;
num1=0;
}
}
}
for(int i=1;i<n;i++){
printf("%lld ",pp[i]);
}
printf("%lld",pp[n]);
return 0;
}
错误次数:3次
原因:方向性错误。
原因:思路不够清晰,导致多处细节错误。
Problem J. Leaking Roof
tag:
⇔排序、⇔模拟
题意:
对于一个 \(n*n\) 的二维矩阵,给定每一个点的高度 \(h\) 。现在,每一个点上均有 \(m\) 升水,水会自高的点平均分配,流向四周的更低的点(只会朝上下左右四个方向流)。如果有一个点的高度为 \(0\) ,则水会漏下。现在请求出每个点分别会有多少升水漏下。
思考过程:
刚开始觉得这道题是BFS,结果由于BFS的不太熟练以及对题意理解的偏差,迟迟没能完成代码。
而后团队转变思路,使用排序+四个方向遍历的方法解决了这道题。
正解:
出题官方将此题定义为简单题。
官方解题思路与我们的思路没有太大区别。
AC代码:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int maxn=505;
#define ms(a,b) memset(a,b,sizeof (a))
double a[maxn][maxn];
struct fff{
int h;
int x;
int y;
}xx[maxn*maxn+10];
bool cmp(fff a, fff b){
return a.h<b.h;
}
LL n,m,v[4],nn[4]={1,-1,0,0},mm[4]={0,0,1,-1},hh[maxn][maxn];
int main(){
scanf("%lld%lld",&n,&m);
LL num=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
a[i][j]=m*1.0;//水
scanf("%lld",&xx[num].h);
hh[i][j]=xx[num].h;//高度
xx[num].x=i;
xx[num].y=j;
num++;
}
}
sort(xx+1,xx+1+n*n,cmp);
for(int i=n*n;i>=1;i--){
num=0;
ms(v,0);
for(int j=0;j<=3;j++){
if ((xx[i].x+nn[j]>=1&&xx[i].x+nn[j]<=n)&&(xx[i].y+mm[j]>=1&&xx[i].y+mm[j]<=n)&&(xx[i].h>hh[xx[i].x+nn[j]][xx[i].y+mm[j]])){
v[j]=1;
num++;
}
}
for(int j=0;j<=3;j++){
if (v[j]){
a[xx[i].x+nn[j]][xx[i].y+mm[j]]+=(a[xx[i].x][xx[i].y])/num;
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if (hh[i][j]==0){
printf("%.6f ",a[i][j]);
}else{
printf("0 ");
}
}
printf("\n");
}
return 0;
}
错误次数:0次
文 / WIDA 杰 黄sir
2021.09.28成文
首发于WIDA个人博客,仅供学习讨论