AtCoder Beginner Contest 365(A~E)
A
問題陳述
給你一個介於 \(1583\) 和 \(2023\) 之間的整數 \(Y\) 。
求公曆 \(Y\) 年的天數。
在給定的範圍內, \(Y\) 年的天數如下:
- 如果 \(Y\) 不是 \(4\) 的倍數,則為 \(365\) 天;
- 如果 \(Y\) 是 \(4\) 的倍數,但不是 \(100\) 的倍數,則為 \(366\) 天;
- 如果 \(Y\) 是 \(100\) 的倍數,但不是 \(400\) 的倍數,則為 \(365\) 天;
- 如果 \(Y\) 是 \(400\) 的倍數,則為 \(366\) 天。
Solution
#include <bits/stdc++.h>
using namespace std;
int n;
int main(){
scanf("%d",&n);
if(n%400==0 || (n%4==0 && n%100!=0)) printf("366");
else printf("365");
return 0;
}
B
問題陳述
給你一個長度為 \(N\) 的整數序列 \(A=(A_1,\ldots,A_N)\)。這裡, 都 \(A=(A_1,\ldots,A_N)\) 是不同的。
在 \(A\) 中,哪個元素是第二大元素?
Solution
維護最大值和第二大的值即可。時間複雜度 \(O(n)\) 。
#include <bits/stdc++.h>
using namespace std;
const int N=105;
int n,a[N],max1,max2;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
if(a[i]>max1) max2=max1,max1=a[i];
else if(a[i]>max2) max2=a[i];
}
for(int i=1;i<=n;++i){
if(a[i]==max2){
printf("%d",i);
return 0;
}
}
return 0;
}
C
問題陳述
有 \(N\) 人參加一項活動, \(i\) /人的交通費用是 \(A_i\) 日元。
活動組織者高橋(Takahashi)決定設定交通補貼的最高限額為 \(x\) 。 \(i\) 人的補貼為 \(\min(x, A_i)\) 日元。這裡, \(x\) 必須是一個非負整數。
高橋的預算為 \(M\) 日元,他希望所有 \(N\) 人的交通補貼總額最多為 \(M\) 日元,那麼補貼限額 \(x\) 的最大可能值是多少?
如果補貼限額可以無限大,請報告。
Solution
若補貼限額可以無限大,則 \(\sum_{i=1}^nA_i\le M\) 。
如果不可以無限大, \(x\) 一定越小越好,具有單調性,考慮二分。
時間複雜度 \(O(nlogn)\) 。
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N=2e5+5;
int n,a[N];
LL m,ans,L=1,R=2e14,sum[N];
bool chk(LL x){
int pos=lower_bound(a+1,a+n+1,x)-a-1;
return x*(n-pos)+sum[pos]<=m;
}
int main(){
scanf("%d %lld",&n,&m);
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
}
sort(a+1,a+1+n);
for(int i=1;i<=n;++i){
sum[i]=sum[i-1]+a[i];
}
while(L<=R){
LL mid=L+R>>1;
if(chk(mid)) L=mid+1,ans=mid;
else R=mid-1;
}
if(ans==2e14) printf("infinite");
else printf("%lld",ans);
return 0;
}
D
問題陳述
高橋和青木玩了 \(N\) 次石頭剪刀布。注:在這個遊戲中,石頭贏剪刀,剪刀贏紙,紙贏石頭。
青木的動作由長度為 \(N\) 的字串 \(S\) 表示,字串由 "R"、"P "和 "S "組成。 \(S\) 的 \(i\) -th字元表示青木在 \(i\) -th對局中的棋步:R "表示 "石頭","P "表示 "紙","S "表示 "剪刀"。
高橋的棋步滿足以下條件:
- 高橋從未輸給過青木。
- 對於 \(i=1,2,\ldots,N-1\) ,高橋在 \(i\) 對局中的棋步與他在 \((i+1)\) 對局中的棋步不同。
請確定高橋的最大勝局數。
可以保證存在一個滿足這些條件的高橋的下棋順序。
Solution
考慮動態規劃。設 \(dp_{i,0}\) 表示第 \(i\) 局平局時的最大勝局數, \(dp_{i,1}\) 表示第 \(i\) 局獲勝時的最大勝局數, \(ans_i\) 表示贏 \(S_i\) 的棋步。
若 \(S_i\ne S_{i-1}\) ,則 \(dp_{i,0}=\max(dp_{i,0},dp_{i-1,0})\) 。
若 \(S_i \ne ans_{i-1}\) ,則 \(dp_{i,0}=max(dp_{i,0},dp_{i-1,1})\) 。
若 \(ans_i\ne S_{i-1}\) ,則 \(dp_{i,1}=max(dp_{i,1},dp_{i-1,0}+1)\) 。
若 \(ans_i\ne ans_{i-1}\) ,則 \(dp_{i,1}=max(dp_{i,1},dp_{i-1,1}+1)\) 。
時間複雜度 \(O(n)\) 。
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,dp[N][2];
char s[N],ans[N];
int main(){
scanf("%d %s",&n,s+1);
for(int i=1;i<=n;++i){
if(s[i]=='R') ans[i]='P';
else if(s[i]=='P') ans[i]='S';
else ans[i]='R';
}
dp[1][1]=1;//1表示贏第i局
for(int i=2;i<=n;++i){
if(s[i]!=s[i-1]) dp[i][0]=max(dp[i][0],dp[i-1][0]);
if(s[i]!=ans[i-1]) dp[i][0]=max(dp[i][0],dp[i-1][1]);
if(ans[i]!=s[i-1]) dp[i][1]=max(dp[i][1],dp[i-1][0]+1);
if(ans[i]!=ans[i-1]) dp[i][1]=max(dp[i][1],dp[i-1][1]+1);
}
printf("%d",max(dp[n][0],dp[n][1]));
return 0;
}
E
問題陳述
給你一個長度為 \(N\) 的整數序列 \(A=(A_1,\ldots,A_N)\) 。求以下表示式的值:
關於位向 XOR 的說明 非負整數 \(A\) 和 \(B\) 的位向 XOR 定義如下,記為 \(A \oplus B\) :
- 在 \(A \oplus B\) 的二進位制表示中,當且僅當 \(A\) 和 \(B\) 的二進位制表示中 \(2^k\) 位上的數字是 \(1\) 時, \(2^k\) ( \(k \geq 0\) )位上的數字是 \(1\) ;否則,它是 \(0\) 。
例如, \(3 \oplus 5 = 6\) (二進位制: \(011 \oplus 101 = 110\) )。
一般來說, \(k\) 個整數 \(p_1, \dots, p_k\) 的位元 XOR 定義為 \((\cdots ((p_1 \oplus p_2) \oplus p_3) \oplus \cdots \oplus p_k)\) 。可以證明這與 \(p_ 1, \dots, p_k\) 的階數無關。
Solution
對於區間 \([L,R]\) ,異或和的二進位制結果的第 \(i\) 位為 \(1\) 的充要條件是 \(A_L\) 到 \(A_R\) 的所有數中第 \(i\) 位是 \(1\) 的數有奇數個。
設 \(cnt_{i,j}\) 表示前 \(i\) 個數中第 \(j\) 位是 \(1\) 的數的個數,那麼\(A_L\) 到 \(A_R\) 的所有數中第 \(i\) 位是 \(1\) 的數的個數可以表示為
列舉右端點 \(R\) ,若 \(L\) 滿足 \(cnt_{L-1,i}\) 與 \(cnt_{R,i}\) 奇偶性不同,則 \(2^i\) 對答案有貢獻。對於每個 \(R\) ,只需記錄 \(L\) 的個數即可。
時間複雜度 \(O(33n)\) 。
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=2e5+5;
int n,a[N],cnt[N][40];
LL ans,cnt0,cnt1,sum;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
sum+=a[i];
for(int j=0;j<=32;++j){
cnt[i][j]=cnt[i-1][j];
if(a[i]&(1ll<<j)) cnt[i][j]++;
cnt[i][j]%=2;
}
}
// for(int i=1;i<=n;++i){
// for(int j=0;j<=3;++j){
// printf("%d ",cnt[i][j]);
// }
// printf("\n");
// }
for(int i=0;i<=32;++i){
cnt0=1,cnt1=0; //cnt0=1將左端點為1的加上
for(int j=1;j<=n;++j){
if(cnt[j][i]==1){
cnt1++;
ans+=cnt0*(1ll<<i);
}
else{
cnt0++;
ans+=cnt1*(1ll<<i);
}
}
}
printf("%lld",ans-sum);
return 0;
}