AtCoder Beginner Contest 273 A-F题解
rank447,算是不错了。
赛时过到 E ,赛后准备补到 G。
A
求 \(n!\)。
Solution
根据题意搜索或递推即可。
Code
int f(int x){
if(x==0)return 1;
return x*f(x-1);
}
int main(){
int n;read(n);
cout<<f(n);
return 0;
}
B
给定一个整数,要求对它每一位分别四舍五入。
Solution
先把要求的那一位后面的全除掉,如果需要进位,就把得到的数加 \(1\),再还原回去。
Code
手写快速幂。
int x,k;
read(x);read(k);
for(int i=1;i<=k;i++){
int k=x/Pow(10,i-1)%10;//取出那一位,判断要不要进位
x/=Pow(10,i);x+=(k>4);
x*=Pow(10,i);
}
cout<<x;
C
求出对于每个 \(k\in[0,k-1]\),有多少个数满足恰好有 \(k\) 种数大于它。
Solution
先离散化后记为 \(t_i\),设值域为 \(len\),求出每个值是否对应有数,并求前缀和,记为 \(s_i\),考虑每个数对答案的贡献,比它大的数的个数就是 \(s_{len}-s_{t_i}\),将答案数组加上即可,具体见代码。
Code
#include<bits/stdc++.h>
#define int long long
#define fi first
#define se second
using namespace std;
using ll=long long;
using pii=pair<int,int>;
using pll=pair<ll,ll>;
using ull=unsigned long long;
inline void read(int &x){
char ch=getchar();
int r=0,w=1;
while(!isdigit(ch))w=ch=='-'?-1:1,ch=getchar();
while(isdigit(ch))r=(r<<1)+(r<<3)+(ch^48),ch=getchar();
x=r*w;
}
const int N=2e5+7;
int a[N],b[N],t[N],s[N],ans[N],n,len;
void init(){
for(int i=1;i<=n;i++)b[i]=a[i];
sort(b+1,b+n+1);
len=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;i++)
t[i]=lower_bound(b+1,b+len+1,a[i])-b,s[t[i]]=1;
for(int i=1;i<=len;i++)s[i]+=s[i-1];
}
main(){
read(n);
for(int i=1;i<=n;i++)read(a[i]);
init();
for(int i=1;i<=n;i++)
ans[s[len]-s[t[i]]]++;
for(int i=0;i<n;i++)printf("%lld\n",ans[i]);
return 0;
}
D
有 L
R
U
D
四种操作,每次询问操作 \(l_i\) 次,遇到墙则停下,问会到达哪个位置。
Solution
很明显需要对于每一行列记录墙的位置,并且需要排好序方便二分查找,用 map 套 set 即可。
每次二分找到当前方向离当前位置最近的墙,看走 \(l_i\) 步会不会被拦下来。
因为不能走出范围,这就很恶心,为了避免大量的特判,我们可以对于行,将 \(0\) 和 \(m+1\) 设为墙,对于列,将 \(0\) 和 \(n+1\) 设为墙,这样代码会好写很多。
Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
void read(int &x)
{
char ch=getchar();
int r=0,w=1;
while(!isdigit(ch))w=ch=='-'?-1:1,ch=getchar();
while(isdigit(ch))r=(r<<3)+(r<<1)+(ch^48),ch=getchar();
x=r*w;
}
map<int,set<int> >f1;
map<int,set<int> >f2;
signed main()
{
int n,m,sx,sy,t,q;
read(n),read(m),read(sx),read(sy);
read(t);
while(t--)
{
int x,y;
read(x),read(y);
f1[x].insert(y);
f2[y].insert(x);
}
read(q);
while(q--)
{
f1[sx].insert(0);f1[sx].insert(m+1);
f2[sy].insert(0);f2[sy].insert(n+1);//将边界设为墙
char op;
int d,tx,ty;
cin>>op;read(d);
if(op=='L')
{
ty=*--f1[sx].lower_bound(sy);
if(sy-ty-1>=d)sy-=d;
else sy=ty+1;
}
if(op=='R')
{
ty=*f1[sx].upper_bound(sy);
if(ty-sy-1>=d)sy+=d;
else sy=ty-1;
}
if(op=='U')
{
tx=*--f2[sy].lower_bound(sx);
if(sx-tx-1>=d)sx-=d;
else sx=tx+1;
}
if(op=='D')
{
tx=*f2[sy].upper_bound(sx);
if(tx-sx-1>=d)sx+=d;
else sx=tx-1;
}
printf("%lld %lld\n",sx,sy);
}
return 0;
}
E
题面很长,就不放简要题面了。
Solution
看起来很难,不过是个诈骗题,你直接用双向指针暴力模拟,然后开几个数组去存时间线就可以了。
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline void read(int &x){
char ch=getchar();
int r=0,w=1;
while(!isdigit(ch))w=ch=='-'?-1:1,ch=getchar();
while(isdigit(ch))r=(r<<3)+(r<<1)+(ch^48),ch=getchar();
x=r*w;
}
const int N=1e6+7;
int fa[N],pre[N],f[N],cnt;
map<int,int>mp;
main()
{
int q;
read(q);
f[0]=-1;
for(int i=1;i<=q;i++)
{
string s;int x;
cin>>s;
if(s[0]=='A'){
read(x);
fa[i]=++cnt;
f[cnt]=x;pre[cnt]=fa[i-1];
}
if(s[0]=='D')fa[i]=pre[fa[i-1]];
if(s[0]=='S'){
read(x);
fa[i]=fa[i-1];mp[x]=i;
}
if(s[0]=='L'){
read(x);
fa[i]=fa[mp[x]];
}
printf("%lld\n",f[fa[i]]);
}
return 0;
}
F
平面上有墙和锤子,一个锤子对应只能敲掉一面墙,最开始在原点,给定终点,问最小步数。
Solution
赛时降智,一个简单 DP 应该要过的,客观原因是因为改 D 改了很久。
首先坐标较大可以离散化掉,设 \(f(i,j,0/1)\) 表示在区间 \([i,j]\) 的左端或右端的最小答案,然后就写完了,暴力转移即可。在区间扩展的时候,如果扩展到的是墙,要判断当前区间是否包含敲掉那面墙的锤子。
Code
代码贺的。
#include<bits/stdc++.h>
#define int long long
#define fi first
#define se second
using namespace std;
using ll=long long;
using pii=pair<int,int>;
using pll=pair<ll,ll>;
using ull=unsigned long long;
inline void read(int &x){
char ch=getchar();
int r=0,w=1;
while(!isdigit(ch))w=ch=='-'?-1:1,ch=getchar();
while(isdigit(ch))r=(r<<1)+(r<<3)+(ch^48),ch=getchar();
x=r*w;
}
const int N=3e3+7;
int n,T,s[N],t[N],x[N],m,nd[N];
int f[N][N][2];
main(){
read(n);read(T);
for(int i=1;i<=n;i++)read(t[i]),x[++m]=t[i];
for(int i=1;i<=n;i++)read(s[i]),x[++m]=s[i];
x[++m]=0,x[++m]=T;
sort(x+1,x+m+1);m=unique(x+1,x+m+1)-x-1;
for(int i=1;i<=n;i++)s[i]=lower_bound(x+1,x+m+1,s[i])-x;
for(int i=1;i<=n;i++)t[i]=lower_bound(x+1,x+m+1,t[i])-x,nd[t[i]]=i;
int S=lower_bound(x+1,x+m+1,0)-x;
T=lower_bound(x+1,x+m+1,T)-x;
memset(f,63,sizeof f);
f[S][S][0]=f[S][S][1]=0;
for(int len=1;len<m;len++)
for(int l=1;l<=m;l++){
int r=l+len-1;
if(l>1){
int p=nd[l-1];
if(!p||(p&&l<=s[p]&&s[p]<=r)){
f[l-1][r][0]=min(f[l-1][r][0],f[l][r][0]+x[l]-x[l-1]);
f[l-1][r][0]=min(f[l-1][r][0],f[l][r][1]+x[r]-x[l-1]);
}
}
if(r<m){
int p=nd[r+1];
if(!p||(p&&l<=s[p]&&s[p]<=r)){
f[l][r+1][1]=min(f[l][r+1][1],f[l][r][0]+x[r+1]-x[l]);
f[l][r+1][1]=min(f[l][r+1][1],f[l][r][1]+x[r+1]-x[r]);
}
}
}
int res=1e18;
for(int i=1;i<=T;i++)
for(int j=T;j<=m;j++)
res=min({res,f[i][j][0],f[i][j][1]});
if(res==(int)1e18)puts("-1");
else printf("%lld\n",res);
return 0;
}