比賽相關信息
比賽信息:
比賽名稱: 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個人博客,僅供學習討論