# Codeforces Round #938 (div.3) 紀錄 *比賽日期: 04/09* ### 比賽連結 : https://codeforces.com/contest/1955 ## 賽中作答 > 賽中答題數:2/8 賽後答題數:7/8 以結果來說,這場比賽實在慘不忍睹ˊnˋ,雖然主要原因是我跑去先寫後面幾題,導致後來沒時間把前面的題目做完,不過比賽計算分數的方式就是答越多題越高分,這麼做是必須冒著很大的風險的,下次應該確實先把前幾題做完,再來思考後面的題目該怎麼做。 ### 第一題 考慮是否一次就買兩個,如果不超出需求並且比購買一個還划算的話,那麼就盡可能買兩個。 #### 程式碼 ``` cpp= #include<bits/stdc++.h> #define ll long long #define pii pair<ll,ll> #define F first #define S second #define mkp(a,b) make_pair(a,b) #define runrun ios_base::sync_with_stdio(false);cin.tie(0); using namespace std; void solve(){ int n,a,b; cin>>n>>a>>b; cout<<min((n/2)*b+(n%2)*a,n*a)<<"\n"; } int main(){ int t; cin>>t; while(t--){ solve(); } return 0; } ``` ### 第四題 給定兩個陣列a跟b (|a|>|b|),題目要求對於陣列a,找到至少k個數字跟b相同的子序列有幾個。這題要先存下b裡面的數字,再來用雙指針維護a的子序列裡面的數字,就可以達成了,時間複雜度O(n+m)=O(n)。 #### 程式碼 ``` cpp= #include<bits/stdc++.h> #define ll long long #define pii pair<ll,ll> #define F first #define S second #define mkp(a,b) make_pair(a,b) #define runrun ios_base::sync_with_stdio(false);cin.tie(0); using namespace std; void solve(){ int n,m,k,in; cin>>n>>m>>k; map<int,int> disc; vector<int> cnt,vec,U; for(int i=0;i<n;i++){ cin>>in; vec.push_back(in); } int sz=0; for(int i=0;i<m;i++){ cin>>in; if(disc.find(in)==disc.end()){ cnt.push_back(0); U.push_back(0); disc[in]=sz++; } U[disc[in]]++; } int C=0,ans=0; for(int i=0;i<n;i++){ if(i-m>=0&&disc.find(vec[i-m])!=disc.end()){ if(U[disc[vec[i-m]]]>--cnt[disc[vec[i-m]]]) C--; } if(disc.find(vec[i])!=disc.end()){ if(U[disc[vec[i]]]>= ++cnt[disc[vec[i]]]) C++; } if(i>=m-1&&C>=k) ans++; } cout<<ans<<"\n"; } int main(){ runrun int t; cin>>t; while(t--){ solve(); } return 0; } ``` ## 賽後補題 > 賽中答題數:2/8 賽後答題數:6/8 ### 第二題 給定n^2^個數字 (注意n<=500),判斷這些數字可不可以構成向下遞增c,向右遞增d的矩陣。 由於最左上角的數字必為最小,若以這個數字為基準,就能確認其右方和下方的數字是否符合遞增條件,進一步判斷整個矩陣是否符合題目要求。 若採用了枚舉的方式,時間複雜度會高達O(n^4^),這不是我們想要的結果;若先對數字做排列,那麼就可以用二分搜快速找到需要的數字,時間複雜度降為O(n^2^logn)。 #### 程式碼 ``` cpp= #include<bits/stdc++.h> #define ll long long #define pii pair<ll,ll> #define F first #define S second #define mkp(a,b) make_pair(a,b) #define runrun ios_base::sync_with_stdio(false);cin.tie(0); using namespace std; vector<int> vec,check; int bsearch(int tar){ int l=0,r=vec.size()-1,mid; while(l<=r){ mid=(l+r)/2; if(vec[mid]<tar){ l=mid+1; } else{ r=mid-1; } } return l; } void solve(){ int n,a,b,in; cin>>n>>a>>b; vec.clear(); check.clear(); for(int i=0;i<n*n;i++){ cin>>in; vec.push_back(in); check.push_back(0); } sort(vec.begin(),vec.end()); bool f=1; int tm=vec[0]; for(int i=0;i<n;i++){ int p2=bsearch(tm)-1; if(vec[p2+1]==tm){ while(p2<n*n-1&&check[++p2]); if(p2>=n*n||vec[p2]!=tm) {f=0;break;} } else {f=0;break;} check[p2]=1; for(int j=1;j<n;j++){ int p3=bsearch(tm+j*b)-1; if(vec[p3+1]==tm+j*b){ while(p3<n*n-1&&check[++p3]); if(p3>=n*n||vec[p3]!=tm+j*b) {f=0;break;} } else {f=0;break;} check[p3]=1; if(!f) break; tm+=a; } if(f) cout<<"Yes\n"; else cout<<"No\n"; } int main(){ int t; cin>>t; while(t--){ solve(); } return 0; } ``` ### 第三題 注意!!有海怪襲擊! 有一列總數n艘戰艦的轟炸戰隊,每艘戰艦有各自的耐久度ai,海怪會攻擊輪流攻擊最靠左/右的戰艦,每次攻擊會讓一艘戰艦減少1點耐久度,若耐久度降至0,那麼那艘戰艦就會沉入海底。 給定海怪總共攻擊的次數k,目標是記錄總共有幾艘戰艦被擊沉。 為了加快計算的速度,我們要在O(1)的時間計算一艘戰艦被擊沉需要花費的攻擊,總時間複雜度便是O(n)。 #### 程式碼 ``` cpp= #include<bits/stdc++.h> #define ll long long #define pii pair<ll,ll> #define F first #define S second #define mkp(a,b) make_pair(a,b) #define runrun ios_base::sync_with_stdio(false);cin.tie(0); using namespace std; ll ans; vector<ll> vec; void Check(ll &l,ll &r){ if(vec[l]==0){ ++l; ++ans; } if(vec[r]==0){ --r; ++ans; } } void solve(){ ll n,k,in; cin>>n>>k; vec.clear(); for(int i=0;i<n;i++){ cin>>in; vec.push_back(in); } ll l=0,r=n-1,cnt=1; ans=0; while(1){ if(l>r) break; else if(l==r){ if(vec[l]<=k){ ans++; } break; } ll m=min(vec[l],vec[r]); if(2*m<=k){ k-=2*m; vec[l]-=m; vec[r]-=m; Check(l,r); } else{ ll m1=k/2; if(k%2){ vec[l]-=m1+1; vec[r]-=m1; } else{ vec[l]-=m1; vec[r]-=m1; } Check(l,r); break; } } cout<<ans<<"\n"; } int main(){ runrun int t; cin>>t; while(t--){ solve(); } return 0; } ``` ### 第五題 一道Greedy題加上一點變數的應用,其實寫這題時原本有用到線段樹~~蠻毒瘤的~~,結果剛好被測資的範圍卡住了,吃了TLE。稍微參考官方的Solution後,才改成現在這種樣式。 #### 程式碼 ``` cpp= #include<bits/stdc++.h> #define ll long long #define pii pair<ll,ll> #define F first #define S second #define mkp(a,b) make_pair(a,b) #define runrun ios_base::sync_with_stdio(false);cin.tie(0); using namespace std; int n; bool func(vector<bool> b,int l){ int cnt=0; int p=0; vector<int> paper; for(int i=0;i<n;i++){ if(p<paper.size()&&paper[p]==i) {cnt--;p++;} if(!(b[i]^(cnt%2))&&i<=n-l){ cnt++; paper.push_back(i+l); } b[i]=b[i]^(cnt%2); } bool t=1; for(int i=0;i<n;i++){ t=t&b[i]; } return t; } void solve(){ char ch; cin>>n; vector<bool> N; for(int i=0;i<n;i++){ cin>>ch; N.push_back((ch=='1')? 1:0); } int L=0,R=n-1,M,ans=1; for(int i=1;i<=n;i++){ if(func(N,i)) ans=i; } cout<<ans<<"\n"; } int main(){ int t; cin>>t; while(t--){ solve(); } return 0; } ``` ### 第六題 \\\\英文閱讀能力大爆炸// 這題我真的卡了很久,從比賽時就看不懂這題在幹嘛,一直到寫這題心得前還是不懂,後來我跑去看有人寫的中文題解後,才總算搞懂這題在做什麼,而且感覺還挺簡單的? 要讓計算的結果為零,我們可以知道,每種數字剩餘的數量無非都是偶數,不然就是123都得是奇數,所以我們只要想辦法找到可以獲勝最多次的貪心策略,就可以得到答案了。 #### 程式碼 ``` cpp= #include<bits/stdc++.h> #define ll long long #define pii pair<ll,ll> #define F first #define S second #define mkp(a,b) make_pair(a,b) #define runrun ios_base::sync_with_stdio(false);cin.tie(0); using namespace std; int ar[5]; void solve(){ int cnt=0,tail=0; for(int i=1;i<=4;++i){ cin>>ar[i]; if(ar[i]>0&&tail==0) tail=i; cnt+=ar[i]; } int s=0,ans=0; for(int i=1;i<=4;++i){ if(ar[i]%2){ s=s^i; } } while(cnt){ if(s==0) ++ans; for(int i=4;i>=1;--i){ if(i==tail){ s=s^i; --ar[i]; break; } else if(ar[i]>0&&(s^i)==0){ s=s^i; --ar[i]; break; } else if(i==4&&ar[i]%2){ s=s^i; --ar[i]; break; } else if(ar[i]>0&&s==0){ s=s^i; --ar[i]; break; } } --cnt; } cout<<ans<<"\n"; } int main(){ int t; cin>>t; while(t--){ solve(); } return 0; } ``` ### 第七題 這題就是二維DP和GCD實作結合在一起的題目,以DP來說算是比較基本的題型。我覺得比較困難地方是執行時間的評估。 這題的我的作法時間複雜度是O(nmk),k是初始值的因數數量,其實蠻難判斷是否合理(至少對我來說),不過就結果來說,這樣的作法被允許的。 這題還有一個我沒有注意到的點,我做狀態轉移的時候,是紀錄當下可以延伸最遠的最大公因數,如果這麼做,還要花更多時間在找最大公因數,對已經緊繃的執行時間又上了一道枷鎖。 換一個想法,只記錄當前選擇的數字能否繼續延伸,這樣不但意圖更明確,又避免了不必要的計算,執行速度更是比前面的作法快了四倍。 #### 程式碼 ```cpp= #include<bits/stdc++.h> #define ll long long #define pii pair<ll,ll> #define F first #define S second #define mkp(a,b) make_pair(a,b) #define runrun ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0); using namespace std; int n,m; bool dp[101][101]; vector<vector<int> > road; bool func(int pre){ for(int i=0;i<=n;++i){ for(int j=0;j<=m;j++){ dp[i][j]=0; } } dp[1][1]=1; for(int i=1;i<=n;++i){ for(int j=1;j<=m;++j){ if(i+j==2) continue; if(road[i][j]%pre==0) dp[i][j]=dp[i][j-1]|dp[i-1][j]; } } return dp[n][m]; } void solve(){ cin>>n>>m; road.assign(n+1,vector<int>(m+1)); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ cin>>road[i][j]; } } vector<int> vec; for(int i=1;i*i<=road[1][1];i++){ if(road[1][1]%i==0){ vec.push_back(i); vec.push_back(road[1][1]/i); } } sort(vec.begin(),vec.end()); int p=vec.size(),ans; while(!(func(vec[--p]))); ans=vec[p]; cout<<ans<<"\n"; } int main(){ runrun int t; cin>>t; while(t--){ solve(); } return 0; } ``` *截稿日期 : 04/30*