AtCoder Beginner Contest 365
AtCoder Beginner Contest 365
A - Leap Year
给出年份,判断这一年有多少天
闰年条件已经给出,逐条判断模拟即可。
#include<iostream>
using namespace std;
int main()
{
int y;
cin>>y;
if(y%400==0||y%4==0&&y%100!=0)
cout<<366<<endl;
else
cout<<365<<endl;
return 0;
}
B - Second Best
求序列中第\(2\)大的数在原序列中是第几个元素。
双指针记录最大值和次大值,扫一遍更新即可。
#include<iostream>
int n,a[105];
using namespace std;
int main()
{
cin>>n;
int maxp=1,smaxp=0;
for(int i=1;i<=n;++i)cin>>a[i];
for(int i=2;i<=n;++i)
{
if(a[i]>a[maxp])smaxp=maxp,maxp=i;
else if(a[i]>a[smaxp])smaxp=i;
}
cout<<smaxp<<endl;
return 0;
}
C - Transportation Expenses
给出一个预算\(M\),求最大的\(x\),使得\(\sum_{i=1}^n \min{(a_i,x)}\le M\)。
二分答案,暴力扫一遍判断即可。
#include<iostream>
#include<cstdio>
using namespace std;
const int MAX=200200;
#define ll long long
int n;
long long m,a[MAX];
int main()
{
scanf("%d",&n);
scanf("%lld",&m);
for(int i=1;i<=n;++i)scanf("%lld",&a[i]);
ll l=0,r=m+1,ret=l;
while(l<=r)
{
ll mid=(l+r)>>1,tot=0;
for(int i=1;i<=n;++i)
tot+=min(a[i],mid);
if(tot<=m)ret=mid,l=mid+1;
else r=mid-1;
}
if(ret>m)
{
puts("infinite");
return 0;
}
printf("%lld\n",ret);
return 0;
}
D - AtCoder Janken 3
两个人玩石头剪刀布,已知第一个人的出招顺序。第二个人不能输任何一局,也不能相邻两局出同样的东西。
问最多能赢几局。
dp。设\(f[i][0/1/2]\)表示当前第\(i\)轮,上一轮出的是什么。转移的时候枚举这轮出什么,判断会不会输、有没有和上轮重复。判断胜负情况然后转移即可。
#include<iostream>
#include<cstdio>
#include<map>
using namespace std;
#define MAX 200200
int n;
char s[MAX];
int f[MAX][3];
map<char,int> M;
int check(int a,int b)
{
if(b==((a+1)%3))return 1;
else return 0;
}
int main()
{
scanf("%d",&n);
scanf("%s",s+1);
f[0][0]=f[0][1]=f[0][2]=0;
M['R']=0;
M['P']=1;
M['S']=2;
for(int i=1;i<=n;++i)
for(int j=0;j<3;++j)
for(int k=0;k<3;++k)
if(j!=k)
if(!check(k,M[s[i]]))
f[i][k]=max(f[i][k],f[i-1][j]+check(M[s[i]],k));
int ans=max(max(f[n][0],f[n][1]),f[n][2]);
printf("%d\n",ans);
return 0;
}
E - Xor Sigma Problem
求\(\sum_{i=1}^{N-1}\sum_{j=i+1}^n \oplus_{k=i}^j A_{k}\)
拆位,然后变成\(01\)序列问题。
设\(f[i]\)表示以\(i\)结尾的、异或和为\(1\)的序列数量。
转移判断这一位是\(1\)还是\(0\)。
最后求和即可。
#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 200200
int n,a[MAX];
long long f[MAX],ans;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%d",&a[i]);
for(int i=0;i<30;++i)
for(int j=1;j<=n;++j)
{
int b=(a[j]>>i)&1;
f[j]=b?(j-1-f[j-1]):f[j-1];
ans+=1ll*(1<<i)*f[j];
f[j]+=b;
}
printf("%lld\n",ans);
return 0;
}
F - Takahashi on Grid
有一个网格图,第\(i\)列只有\(L[i]\)到\(U[i]\)位置可以走,每次给出两个坐标,问最短路。
首先,如果能向右走,就一定向右走。这样子一定不会更差。
于是区间子段可以分成两种:一种是可以一路向右走到顶,不需要沿着上下方向走的,称为\(A\)型。另一种是一路向右会撞墙,不得不通过上下移动走到特定位置才能继续向右,称为\(B\)型。
记录\(A\)型为三元组\((l,r,c)\),\(l,r\)表示贯穿范围,\(c=-1\)用来标识。
记录\(B\)型为三元组\((l,r,c)\),\(l\)表示最佳入口,如果不从这个位置进入,则需要先通过上下移动到达这个位置才能向右继续走(可以证明这个路径不会更差)。\(r\)表示根据一直向右走的条件,迫不得已才上下移动的情况下的出口位置。\(c\)表示区间内在符合一直向右走情况下,从最佳入口到出口的迫不得已的上下移动距离。
发现这两种区间和其三元组表示可以合并。
于是可以使用线段树进行维护。
#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 200200
#define ll long long
#define lson (x<<1)
#define rson (x<<1|1)
int n,Q;
int U[MAX],D[MAX];
struct Node{int l,r;ll c;}t[MAX<<2];
int dis(int i,int p1,int p2)
{
int L=max(D[i],D[i+1]);
int R=min(U[i],U[i+1]);
if(L<=p1&&p1<=R)return 1+abs(p1-p2);
int dis=min(abs(p1-L)+1+abs(L-p2),abs(p1-R)+1+abs(R-p2));
return dis;
}
Node pushup(Node a,Node b)
{
Node ret;
if(a.c==-1)
{
if(b.c==-1)
{
if(max(a.l,b.l)<=min(a.r,b.r))
ret.l=max(a.l,b.l),ret.r=min(a.r,b.r),ret.c=-1;
else
{
if(a.r<b.l)
ret.l=a.r,ret.r=b.l,ret.c=b.l-a.r;
else
ret.l=a.l,ret.r=b.r,ret.c=a.l-b.r;
}
}
else
{
if(a.l<=b.l&&b.l<=a.r)
ret.l=b.l,ret.r=b.r,ret.c=b.c;
else
{
if(a.l>b.l)
ret.l=a.l,ret.r=b.r,ret.c=b.c+a.l-b.l;
else
ret.l=a.r,ret.r=b.r,ret.c=b.c+b.l-a.r;
}
}
}
else
{
if(b.c==-1)
{
if(b.l<=a.r&&a.r<=b.r)
ret.l=a.l,ret.r=a.r,ret.c=a.c;
else
{
if(a.r<b.l)
ret.l=a.l,ret.r=b.l,ret.c=a.c+b.l-a.r;
else
ret.l=a.l,ret.r=b.r,ret.c=a.c+a.r-b.r;
}
}
else
ret.l=a.l,ret.r=b.r,ret.c=a.c+b.c+abs(a.r-b.l);
}
return ret;
}
void Build(int x,int l,int r)
{
if(l==r)
{
t[x]=(Node){D[l],U[l],-1};
return;
}
int mid=(l+r)>>1;
Build(lson,l,mid);
Build(rson,mid+1,r);
t[x]=pushup(t[lson],t[rson]);
}
Node Query(int x,int l,int r,int L,int R)
{
if(L==l&&r==R)return t[x];
int mid=(l+r)>>1;
if(R<=mid)return Query(lson,l,mid,L,R);
if(L>mid)return Query(rson,mid+1,r,L,R);
return pushup(Query(lson,l,mid,L,mid),Query(rson,mid+1,r,mid+1,R));
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%d%d",&D[i],&U[i]);
Build(1,1,n);
scanf("%d",&Q);
while(Q--)
{
int sx,sy,tx,ty;
scanf("%d%d%d%d",&sx,&sy,&tx,&ty);
if(sx>tx)swap(sx,tx),swap(sy,ty);
if(sx==tx)
{
printf("%d\n",abs(sy-ty));
continue;
}
Node a=Query(1,1,n,sx,tx);
a=pushup((Node){sy,sy,-1},a);
a=pushup(a,(Node){ty,ty});
if(a.c==-1)puts("0");
else printf("%lld\n",a.c+tx-sx);
}
return 0;
}
G - AtCoder Office
有若干条出退勤记录,每条记录表示一个人状态变更(出勤变退勤,退勤变出勤)。
每次给出两个人,问这两个人同时出勤时长。
分块。
对于出退勤记录较少的两个人,维护双指针暴力计算线段交。
对于出退勤记录较多的单人,先用前缀和维护其出勤时间,剩下所有人通过前缀和计算出共事的时间。
复杂度\(O(M\sqrt M)\)。
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
#define MAX 200200
int read(){int x;scanf("%d",&x);return x;}
int n,m,q;
int cnt[MAX],tot,ID[MAX];
vector<int> t[MAX];
int Ans[1005][MAX];
int T[MAX],P[MAX];
int tmp[MAX];
int main()
{
n=read();m=read();
for(int i=1;i<=m;++i)
{
T[i]=read(),P[i]=read();
++cnt[P[i]];
t[P[i]].push_back(i);
}
for(int i=1;i<=n;++i)
if(cnt[i]>=500)
{
ID[i]=++tot;
for(int j=1,f=0;j<=m;++j)
{
if(f)tmp[j]=T[j]-T[j-1];
else tmp[j]=0;
if(P[j]==i)f^=1;
}
for(int j=1;j<=m;++j)tmp[j]+=tmp[j-1];
for(int j=1;j<=n;++j)
if(i!=j)
if(t[j].size())
for(int k=0,l=t[j].size();k<l;k+=2)
Ans[tot][j]+=tmp[t[j][k+1]]-tmp[t[j][k]];
}
int Q=read();
while(Q--)
{
int a=read(),b=read();
if(ID[a])
{
printf("%d\n",Ans[ID[a]][b]);
continue;
}
if(ID[b])
{
printf("%d\n",Ans[ID[b]][a]);
continue;
}
int p1=0,p2=0,l1=t[a].size(),l2=t[b].size();
int f1=0,f2=0,lt=0,ans=0;
while(p1<l1&&p2<l2)
{
if(t[a][p1]<t[b][p2])
{
if(f1&&f2)ans+=T[t[a][p1]]-lt;
f1^=1;
lt=T[t[a][p1]];
p1++;
}
else
{
if(f1&&f2)ans+=T[t[b][p2]]-lt;
f2^=1;
lt=T[t[b][p2]];
p2++;
}
}
printf("%d\n",ans);
}
return 0;
}