0 简介

0-1 关于文章

这是 AtCoder Beginner Contest 273 的解释。
该实现是用 Python 和 C++ 编写的。
请注意,可能与官方解释存在差异。
如果您发现任何错误,请在评论部分告诉我。

1 ABC273 评论

1-1 个人印象

我感冒了。 ABC273をPythonとC++で
Diff就像A问题是灰色的前半部分,B是灰色的后半部分,C是棕色的前半部分,D是绿色的后半部分,E是蓝色的前半部分。
80分钟拿下4冠,所以有段时间第一次变成绿色Perf,大概4冷。 .
我很抱歉,因为我在 9 分钟内赢得了三冠王。

1-2 问题 A 递归函数

问题

非负整数 $x$ 上的函数 $f(x)$ 满足

・$f(0)=1$
・对于任何正整数$k$,$f(k)=k・f(k-1)$

求 $f(N)$。

约束

・$1 leq N leq 10$

评论

换句话说,找到$N$ 的阶乘。
因此,根据定义,$N$的阶乘是$1$到$N$的自然数的总乘积,所以可以使用for语句求解。

Python中的实现示例
N=int(input())
ans=1
for i in range(1,N+1):
  ans*=i
print(ans)
C++中的实现示例
#include <bits/stdc++.h>
using namespace std;
#define rep(i,N,M) for(int i=N; i<M; i++)

int main(){
  int N; cin>>N;
  int ans=1;
  rep(i,1,N+1){
    ans*=i;
  }
  cout<<ans<<endl;
}

int 类型也可以接受。 (从 $10 geq N$ 起,最高 $3628800$。)

将问题名称翻译成日语是一个递归函数。
也可以使用递归函数,但 for 语句更明智。

1-3 问题 B 不完整的舍入

问题

对于非负整数$X$,按照$i=0,1,...K-1$的顺序进行以下操作,求$X$的最终值。

・将$X$ 舍入到最接近的$10^i$。

约束

・$0 leq X < 10^{15}$
・$1 leq K leq 15$

评论

这是一个四舍五入的操作。
四舍五入在每种语言的标准库中都可用。
要将整数 $n$ 舍入到最近的 $10^i$ 位置,只需将 $n$ 除以 $10^i$ 并四舍五入到整数...

但是,如果是十进制数,可能会受到误差的影响,所以让我们创建自己的舍入函数。

将$n$四舍五入到最接近的$10^i$的操作定义为“如果$n$到最接近的$10^i$的部分是$m$,并且$m$大于$10^i的一半$,向上取整,否则向下取整。”
在数学上,它可以表示如下。 (四舍五入的值 $=n_2$)

・$m=mod(n,10^{i+1})$($mod(a,b) 是 a 除以 b 的余数)
・如果$m<5・10^i$,则$n_2=n-m$(向下取整)
・如果不是,$n_2=n-m+10^{i+1}$(四舍五入)

您应该按原样实施。

Python中的实现示例
X,K=map(int,input().split())

def calc(m,i):
  m=m%(10**(i+1))
  if m<(10**i)*5:
    return m-m
  else:
    return m-m+(10**(i+1))

for i in range(K):
  X=calc(X,i)
print(X)
C++中的实现示例
#include <bits/stdc++.h>
using namespace std;
#define rep(i,N,M) for(int i=N; i<M; i++)
using ll=long long;

ll pw(ll x,int n){
  ll ret=1LL;
  rep(i,0,n){
    ret*=x;
  }
  return ret;
}

ll calc(ll N,int i){
  i=(double)i;
  ll n=N%pw(10LL,i+1);
  if(n<pw(10LL,i)*5){
    return N-n;
  }else{
    return N-n+pw(10LL,i+1);
  }
}

int main(){
  ll X; int K; cin>>X>>K;
  
  rep(i,0,K){
    X=calc(X,i);
  }
  
  cout<<X<<endl;
}

$X$ 的最大值约为 $10^{15}$,因此对于 C++,请使用 long long
还有,求幂函数pow返回的是十进制数,比较麻烦,所以自己做了个函数。

在为此类问题做准备时,在库中进行四舍五入可能会很方便。

1-4 C 问题 (K+1)-th 最大数

问题

给定一个长度为 $N$ 的整数序列 $A$。
回答 $K=0,1,2,...N-1$ 的下列子问题。

假设$A$中有$K$种大于$A_i$的数,$i$有多少种可能的数?

约束

・$1 leq N leq 2×10^5$
・$1 列克 A_i 列克 10^9$

评论

如果您诚实计算,则需要 $O(N^2)$,并且为时已晚。
独创性。

如果$A$ 中有$K$ 种大于$A_i$,则$A_i$ 是$A$ 中第$K+1$ 个最大的。所以子问题可以重述为“第 $K+1$th 最大的数是多少?”

(假设 $B$ 为 $A$ 并删除重复项)
您所要做的就是准备一个字典来存储序列中每个数字的编号,并以$B$ 的降序引用该字典。
此外,由于这仅输出具有元素数量 $ 的 $K leq B,因此它输出具有元素数量 $N-B 的 $0$ 次。

准备字典需要$O(N)$,排序需要$O(N log N)$,所以总的计算复杂度是$O(N log N)$。

Python中的实现示例
from collections import Counter

N=int(input())
A=list(map(int,input().split()))

cnt=Counter(A)
B=sorted(list(set(A)))[::-1]
for n in B:
  print(cnt[n])
for i in range(N-len(B)):
  print(0)
C++中的实现示例
#include <bits/stdc++.h>
using namespace std;
#define rep(i,N,M) for(int i=N; i<M; i++)

int main(){
  int N; cin>>N;
  
  vector<int> A(N),B;
  map<int,int> cnt;
  rep(i,0,N){
    cin>>A[i];
    if(!cnt.count(A[i])){
      B.push_back(A[i]);
    }
    cnt[A[i]]++;
  }
  
  sort(B.rbegin(),B.rend());
  for(auto n:B){
    cout<<cnt[n]<<endl;
  }
  rep(i,0,N-B.size()){
    cout<<0<<endl;
  }
}
这是在字典类型预处理中将$O(N^2)$ 转换为$O(N)$ 的问题。 习惯处理字典。

1-5 D问题LRUD说明

问题

我有一个 $H×W$ 网格。从上数第 $i$ 行和从左数第 $j$ 列的单元格表示为 $(i,j)$。
$N$ 个方块被障碍物阻挡。
首先你在正方形 $(r_s,c_s)$ 上。
您会收到 $Q$ 查询。

沿着 $d_i$ 的方向走 $l_i$ 个方块,直到遇到障碍物。

完成每个查询后,打印您所在的正方形。

约束

・$2 leq H, W leq 10^9$
・$1 leq r_s leq H, 1 leq c_s leq W$
・$0 leq N leq 2×10^5$
・$1 leq r_i leq H, 1 leq c_i leq W$
・$(r_s,c_s)$ 和 $(r_i,c_i)$ 之间没有重叠
・$1 leq Q leq 10^5$
・$1 列克 l_i 列克 10^9$

评论

如果简单移动多次,计算量为$O(sum l)$,显然是不够的。
让我们发挥创意。

移动查询是“如果前方有障碍物 $l_i$,正面停下来,如果不前进$l_i$。”
L,R,U,D 之后的单元格可以这样表示。 (以 $(x,y)$ 作为当前位置)

L : $(x,max(y-d,y+1 左侧最近的障碍物)$
R : $(x,min(y+d,y-1 右侧最近的障碍物)$
U : $(max(x-d, x +1 上方最近的障碍物), y)$
D : $(min(x+d,x-1 以下最近故障),y)$

通过对存储每一行​​和每一列的障碍物方块的数组进行二分查找,得到要寻找的“最靠近左/右的障碍物”。 (如果该行/列中没有障碍物,您可以继续。)

预计算需要 $O(N log N)$ 才能将故障单元格存储在每一行/列中。
(不是 $O(N)$ 因为它排序)
该查询使用 $(log N)$,因为它一次对大小为 $N$ 的数组执行二进制搜索。
总复杂度是 $O((N+Q) log N)$,这很好。

你应该实现这个。
使用bisect.bisect_left 表示Pythonstd::lower_bound 表示C++ 用于二分查找部分。
另外,请注意不要因移动而脱离网格。
$1 leq i leq H,1 leq j leq W$ 为 $(i,j)$。

Python中的实现示例
import sys
def input():
  return sys.stdin.readline().rstrip()
from bisect import bisect_left as bile

H,W,rs,cs=map(int,input().split())
N=int(input())
xn,yn={},{} #xn[x]はx行目、yn[y]はy列目に存在する障害物
xs,ys=set(),set() #xs,ysは障害物のない行、列
for i in range(N):
  x,y=map(int,input().split())
  xs.add(x); ys.add(y)
  xn.setdefault(x,[]); xn[x].append(y)
  yn.setdefault(y,[]); yn[y].append(x)

for i in xn.keys():
  xn[i].sort()
for i in yn.keys():
  yn[i].sort()

Q=int(input())
x,y=rs,cs #現在地
for i in range(Q):
  d,l=input().split(); l=int(l)
  
  if d=="L":
    if x not in xs:
      y-=l
    else:
      ind=bile(xn[x],y)-1
      if ind==-1: #左に障害物がない
        y-=l
      else:
        y=max(y-l,xn[x][ind]+1)
  elif d=="R":
    if x not in xs:
      y+=l
    else:
      ind=bile(xn[x],y)
      if ind==len(xn[x]): #右に障害物がない
        y+=l
      else:
        y=min(y+l,xn[x][ind]-1)
  elif d=="U":
    if y not in ys:
      x-=l
    else:
      ind=bile(yn[y],x)-1
      if ind==-1: #上に障害物がない
        x-=l
      else:
        x=max(x-l,yn[y][ind]+1)
  else:
    if y not in ys:
      x+=l
    else:
      ind=bile(yn[y],x)
      if ind==len(yn[y]): #下に障害物がない
        x+=l
      else:
        x=min(x+l,yn[y][ind]-1)
  x=max(1,min(x,H))
  y=max(1,min(y,W))
  print(x,y)

这需要很长时间。
我使用比input() 更快的输入工具,因为我有很多输入。

C++中的实现示例
#include <bits/stdc++.h>
using namespace std;
#define rep(i,N,M) for(int i=N; i<M; i++)
#define all(L) (L).begin(),(L).end()

int main(){
  int H,W,rs,cs; cin>>H>>W>>rs>>cs;
  int N; cin>>N;
  map<int,vector<int>> xn,yn;
  set<int> xs,ys;
  rep(i,0,N){
    int x,y; cin>>x>>y;
    xs.insert(x); ys.insert(y);
    xn[x].push_back(y);
    yn[y].push_back(x);
  }
  
  for(auto i:xs) sort(all(xn[i]));
  for(auto i:ys) sort(all(yn[i]));
  
  int Q; cin>>Q;
  int x=rs,y=cs;
  rep(i,0,Q){
    char d; int l; cin>>d>>l;
    int ind;
    if(d=='L'){
      if(!xs.count(x)) y-=l;
      else{
        ind=lower_bound(all(xn[x]),y)-xn[x].begin();
        if(ind==0){ //左に障害物がない
          y-=l;
        }else{
          y=max(y-l,xn[x][ind-1]+1);
        }
      }
    }else if(d=='R'){
      if(!xs.count(x)) y+=l;
      else{
        ind=lower_bound(all(xn[x]),y)-xn[x].begin();
        if(ind==xn[x].size()){ //右に障害物がない
          y+=l;
        }else{
          y=min(y+l,xn[x][ind]-1);
        }
      }
    }else if(d=='U'){
      if(!ys.count(y)) x-=l;
      else{
        ind=lower_bound(all(yn[y]),x)-yn[y].begin();
        if(ind==0){ //上に障害物がない
          x-=l;
        }else{
          x=max(x-l,yn[y][ind-1]+1);
        }
      }
    }else{
      if(!ys.count(y)) x+=l;
      else{
        ind=lower_bound(all(yn[y]),x)-yn[y].begin();
        if(ind==yn[y].size()){ //下に障害物がない
          x+=l;
        }else{
          x=min(x+l,yn[y][ind]-1);
        }
      }
    }
    x=max(1,min(x,H));
    y=max(1,min(y,W));
    cout<<x<<" "<<y<<endl;
  }
}

C++中的二分查找不难吗?

它非常繁重且难以实施。

1-6 E 问题笔记本

问题

我有一个整数列 $A$ 和 $10^9$ 页的笔记。
您会收到 $Q$ 查询。查询是以下之一:

ADD x :将x 添加到整数字符串的末尾。
DELETE :如果整数字符串不为空,则删除它的最后一个元素。
SAVE y :用整数列 $A$ 覆盖 y 页面。
LOAD z :将整数字符串 $A$ 更改为 z 页面上所写的内容。

最初,$A$ 和所有笔记页面都是空的。按顺序执行 $Q$ 查询,在每个查询之后打印 $A$ 的尾随数字。 (-1 如果 $A$ 为空)

约束

・$1 leq Q leq 5×10^5$
・$1 leq x,y,z leq 10^9$

评论

即使我创建一个变量来存储笔记信息,MLETLE也会出现。
我需要一些聪明才智。

添加一个整数,返回之前的信息,或者为每个查询替换之前的 $A$。
树结构可用于此目的。

考虑如何以更少的处理运行查询。

我们想要一个有根树,其中每个顶点都写有一个整数,并且从最高父节点到该顶点的最短路径产生一个以该顶点结尾的序列。

有了这个,每个查询都可以这样实现:

ADD x :将带有x 的子节点附加到当前查看的顶点
DELETE : 移动到查看顶点的父级
SAVE y :在y 页面上记录您正在查看的顶点
LOAD z : 移动到z 页面上记录的顶点

这允许 $O(1)$ 用于每个查询。
总计算复杂度为$O(Q)$。

Python中的实现示例
import sys
def input():
  return sys.stdin.readline().rstrip()

Q=int(input())

pr={0:0} #親
num={0:-1} #頂点に書かれた数
to={} #nページに保存されている頂点
seen=set() #頂点が保存されたページ
now=0 #現在見ている頂点
ans=[]
for i in range(Q):
  query=input().split()
  s=query[0]
  
  if s=="ADD":
    x=query[1]
    nxt=len(num)
    num.setdefault(nxt,0); num[nxt]=x;
    pr.setdefault(nxt,0); pr[nxt]=now;
    now=nxt
  elif s=="DELETE":
    now=pr[now]
  elif s=="SAVE":
    y=query[1]
    to.setdefault(y,0); to[y]=now
    seen.add(y)
  else:
    z=query[1]
    if z in seen:
      now=to[z]
    else:
      now=0
  ans.append(num[now])

print(*ans,sep=" ")

加快您的打字速度。

C++中的实现示例
#include <bits/stdc++.h>
using namespace std;
#define rep(i,N,M) for(int i=N; i<M; i++)

int main(){
  ios::sync_with_stdio(false);
  
  int Q; cin>>Q;
  
  map<int,int> pr,num,to; //親、頂点に書かれた数、nページに保存されている頂点
  pr[0]=0; num[0]=-1; //根の初期化
  set<int> seen; //頂点の保存されたページ
  int now=0; //見ている頂点
  
  rep(i,0,Q){
    string s; cin>>s;
    if(s=="ADD"){
      int x; cin>>x;
      int nxt=num.size();
      num[nxt]=x;
      pr[nxt]=now;
      now=nxt;
    }else if(s=="DELETE"){
      now=pr[now];
    }else if(s=="SAVE"){
      int y; cin>>y;
      to[y]=now;
      seen.insert(y);
    }else{
      int z; cin>>z;
      if(seen.count(z)){
        now=to[z];
      }else{
        now=0;
      }
    }
    cout<<num[now]<<endl;
  }
}

看来std::ios::sync_with_stdio(false)可以加快输入速度。

仅想出蓝色 Diff 是相当困难的。 我无法解决它。 ABC273をPythonとC++で

2 最后

感谢您阅读到最后。
这一次,我认为有很多人($∋me$)在D问题的实现游戏中失败了。
请记住,二进制搜索是一种频繁的算法。
我不知道E。很难。这是一个Flash游戏。
就这样。
拥有美好的职业竞技生活!


原创声明:本文系作者授权爱码网发表,未经许可,不得转载;

原文地址:https://www.likecs.com/show-308629827.html

相关文章: