【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

uTools_1632837130736.png


部分题解与小结

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个人博客,仅供学习讨论


posted @ 2021-10-06 11:13  hh2048  阅读(1993)  评论(0编辑  收藏  举报