【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個人博客,僅供學習討論



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM