A - Leap Year
题目大意
给定 \(n\),求第 \(n\) 年的天数(\(365\) 或 \(366\))。
思路
显然地,我们需要判断这个是否为闰年。
如果 \(n\) 不能被 \(4\) 整除,那么不是闰年。
如果 \(n\) 可以被 \(400\) 整除,那么是闰年。
如果 \(n\) 不可以被 \(100\) 整除但是可以被 \(4\) 整除,那么是闰年。
否则就不是闰年。
很简单。
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int n;cin>>n;
if(n%4!=0){
cout<<365;
}
else if(n%400==0){
cout<<366;
}
else if((n%100!=0)&&n%4==0){
cout<<366;
}
else cout<<365<<endl;
return 0;
}
B - Second Best
题目大意
给定 \(\{a_n\}\),求次大值的位置。
思路
打擂。记目前最大值为 \(max\),目前次大值为 \(cmax\)。
如果 \(a_i>max\),那么把 \(cmax\) 更新为 \(max\),把 \(max\) 更新为 \(a_i\),同时记录位置。
如果 \(a_i<max\) 并且 \(a_i>cmax\),那么把 \(cmax\) 更新为 \(a_i\),同时记录位置。
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int n;cin>>n;
vector<int>a(n+1);
for(int i=1;i<=n;i++)
cin>>a[i];
int ans1=a[1],ans2=0;
int pos1=1,pos2=0;
for(int i=2;i<=n;i++){
if(a[i]>ans1){
ans2=ans1;
pos2=pos1;
ans1=a[i];
pos1=i;
}
else if(a[i]>ans2){
ans2=a[i];
pos2=i;
}
}
cout<<pos2<<endl;
return 0;
}
C - Transportation Expenses
题目大意
给定 \(\{A_N\}\),要求 \(\sum\min\{A_i,x\}\le M\),求最大值 \(x\)(可能为无穷大)。
思路
很板的二分答案。
首先考虑无穷大的答案。可以先计算序列的总和和 \(M\) 比较,如果这个都小于等于 \(M\),那么肯定答案是无穷大。
如果不是无穷大呢?可以试着枚举一个 \(x\),然后对于每一个 \(x\) 判断是否满足题目条件,然后记录最大的 \(x\),时间复杂度 \(O(NM)\)。
考虑二分一个 \(x\) 然后来判断,题目要求 \(x\) 的最大值,可以发现若此时的 \(x\) 为最大,那么对于每一个 \(k\le x\) 都可以满足题目条件,而对于每一个 \(k\ge x\) 都不能满足题目条件,满足单调性,写二分答案,时间复杂度 \(O(N\log M)\)。
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,a[200005],ans=-1e17;
bool check(int x){
int res=0;
for(int i=1;i<=n;i++)
res+=min(a[i],x);
return (res<=m);
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>a[i];
if(check(1e17)){
cout<<"infinite"<<endl;
return 0;
}
int l=0,r=m;
while(l<=r){
int mid=(l+r)>>1;
if(check(mid))ans=mid,l=mid+1;
else r=mid-1;
}
cout<<ans<<endl;
return 0;
}
D - AtCoder Janken 3
题目大意
两个人玩石头剪刀布,已知 \(A\) 的出招顺序,而且 \(B\) 从来没有输给 \(A\),而且 \(B\) 相邻两次的出招是不一样的(比如这次 \(B\) 出了石头,那么他下一次就不能出石头),求 \(B\) 最多可以赢多少局。
思路
因为发现 \(B\) 出招的限制条件只和前一次和后一次的有关,所以这个问题显然满足无后效性,考虑 DP。
设 \(f_{i,0/1}\) 表示已经过了 \(i\) 局并且第 \(i\) 局 \(B\) 与 \(A\) 平局或赢(取 \(0\) 时两人平局,取 \(1\) 时 \(B\) 赢)时的最大胜场数。
显然发现通过判断当前局和上一局的平局和胜利的出招是否相同来进行转移。
具体见代码。
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,f[200001][2]={0};
string s;
char lp,lwin;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>s;
f[0][0]=0,f[0][1]=1;
if(s[0]=='S')lwin='R',lp='S';
else if(s[0]=='R')lwin='P',lp='R';
else lwin='S',lp='P';
for(int i=1;i<n;i++){
char p,win;
if(s[i]=='S')win='R',p='S';
else if(s[i]=='R')win='P',p='R';
else win='S',p='P';
if(p!=lp)f[i][0]=max(f[i-1][0],f[i][0]);
if(p!=lwin)f[i][0]=max(f[i-1][1],f[i][0]);
if(win!=lp)f[i][1]=max(f[i-1][0]+1,f[i][1]);
if(win!=lwin)f[i][1]=max(f[i-1][1]+1,f[i][1]);
lp=p,lwin=win;
}
cout<<max(f[n-1][0],f[n-1][1])<<endl;
return 0;
}
E - Xor Sigma Problem
题目大意
给定 \(\{A_N\}\),求 \(\begin{aligned}\sum_{i=1}^{N-1}\sum_{j=i+1}^NA_i\oplus A_{i+1}\oplus\cdots\oplus A_j\end{aligned}\)。
思路
考虑一下这道题的弱化版:求序列中两两异或的总和,也就是求 \(\begin{aligned}\sum_{i=1}^{N-1}\sum_{j=i+1}^NA_i\oplus A_j\end{aligned}\)。
不难发现,正常求是需要 \(O(N^2)\) 的复杂度,考虑拆位。
也就是把每一个数都拆成二进制位,比如对于 \(A=\{2,3,5,8\}\),那么拆位之后就是:
\[2 : 0\ 0\ 1\ 0 \]\[3 : 0\ 0\ 1\ 1 \]\[5 : 0\ 1\ 0\ 1 \]\[8 : 1\ 0\ 0\ 0 \]可以发现,每一位的贡献其实都是这一位上两两异或为 \(1\) 的贡献。
也就是说,第一位会产生 \(3\) 的贡献(\(3\) 个 \(0\) 和 \(1\) 个 \(1\))。
以此类推,每一位的贡献分别为 \(3\),\(3\),\(4\),\(4\)。
那么乘上每一位的权重,可以得出答案为:
\[3\times2^3+3\times2^2+4\times2^1+4\times2^0=48 \]再来考虑一下原问题,不难发现我们可以预处理出一个异或前缀 \(s\),也就是 \(s_i=s_{i-1}\oplus A_i\),那么类似前缀和,根据异或的自反性质,区间 \([l,r]\) 的异或为 \(s_r\oplus s_{l-1}\)。
也就是说,我们目前得到了所有的区间的异或值,不难看出原题的答案就是:
\[\begin{aligned}\sum^{N}_{i=1}s_i+\sum^{N}_{i=2}{s_i\oplus s_1}+\cdots+\sum^{N}_{i=N-1}{s_i\oplus s_{N-2}}-\sum^N_{i=1}A_i\end{aligned} \]这个式子看起来挺复杂吧?
其实再深入思考一下就可以发现其实中间的那一大串(\(\begin{aligned}\sum^{N}_{i=2}{s_i\oplus s_1}+\cdots+\sum^{N}_{i=N-1}{s_i\oplus s_{N-2}}\end{aligned}\))拆开来看,其实就是 \(s_1\) 到 \(s_N\) 的两两异或!
也就是说,我们可以根据 \(s_i\) 来重新建一个序列,求完了这个序列的两两异或和之后再加上 \(\sum s_i\),然后因为原题中没有包含 \(A_i\) 的情况,所以还要再减去 \(\sum A_i\)。
具体见代码。
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int ans[20]={0};
int b[200001];
int sum[200001]={0};
long long res=0;
int n,a,i,len,mx=-1;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++)cin>>b[i];
for(int i=1;i<=n;i++)sum[i]=sum[i-1]^b[i];
for(i=1;i<=n;i++){
a=sum[i];
len=0;
while(a){
len++;
if(a&1)ans[len]++;
a=a>>1;
}
mx=max(mx,len);
}
for(i=1;i<=mx;i++)
res+=(1ll<<(i-1))*ans[i]*(n-ans[i]);
for(int i=1;i<=n;i++)
res+=sum[i],res-=b[i];
cout<<res;
return 0;
}
标签:AtCoder,cout,Beginner,int,sum,long,异或,oplus,365
From: https://www.cnblogs.com/snapyust/p/18341238