0 简介

0-1 关于文章

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

1 ABC276 评论

1-1 个人印象

这次我也过不了F。
Diff就像A是早灰,B是晚灰,C是中灰棕,D是晚棕,E是晚绿,F是晚水。
看起来不像上周?我认同。
我想通过后半段水。

1-2 问题 A 最右边

问题

您将获得一个小写字符串 $S$。
从 $S$ 的左边算起 $S$ 中最右边的a 的编号。
如果a 不包含在$S$ 中,则输出-1

约束

・$1 列克 |S| 列克 100$

评论

它可以通过执行一个操作来解决,该操作将for 句子围绕S 旋转并在您正在查看的字符是a 时更新答案。
如果从未更新,则输出-1,否则输出答案。

输出时不要忘记使用1-indexed

Python中的实现示例
S=input()

ans=-1
for i in range(len(S)):
  if S[i]=="a":
    ans=i+1
print(ans)

请注意,Python 中的 S.index(n) 返回 S 中包含的第一个 nindex
(如果字符串颠倒了也可以使用..)

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

int main(){
  string S; cin>>S;
  int l=S.size();
  
  int ans=-1;
  rep(i,0,l){
    if(S[i]=='a'){
      ans=i+1;
    }
  }
  cout<<ans<<endl;
}

1-3 问题B邻接表

问题

有 $N$ 个城市,编号为 $1,2,...,N$ 和 $M$ 路连接它们。
第$i$条路连接$A_i、B_i$城市,$A_i、B_i$可以相互通行。
为 $k(1 leq k leq N)$ 打印以下内容。

与城市$k$直接相连的城市$d_k$的数量,以及这些城市编号升序排列的序列$a_k$

约束

・$2 leq N leq 10^5$
・$1 leq M leq 10^5$
・$1 leq A_i < B_i leq N$
・$(A_i,B_i)$ 不同

评论

准备一个数组$L_i$,存储与城市$i$直接相连的城市,进入时记下。令 $L$ 是一个二维数组。
输入完成后,排序输出。

Python中的实现示例
N,M=map(int,input().split())
L=[[] for _ in range(N+1)]
for i in range(M):
  a,b=map(int,input().split())
  L[a].append(b); L[b].append(a)

for i in range(N):
  print(len(L[i+1]),*sorted(L[i+1]),sep=" ")
C++中的实现示例
#include <bits/stdc++.h>
using namespace std;
#define rep(i,N,M) for(int i=N; i<M; i++)

int main(){
  int N,M; cin>>N>>M;
  vector<vector<int>> L(N+1);
  rep(i,0,M){
    int a,b; cin>>a>>b;
    L[a].push_back(b); L[b].push_back(a);
  }
  
  rep(i,1,N+1){
    int n=L[i].size();
    cout<<n<<" ";
    sort(L[i].begin(),L[i].end());
    rep(j,0,n){
      cout<<L[i][j];
      if(j!=n-1){
        cout<<" ";
      }else{
        cout<<endl;
      }
    }
  }
}

用存储连接到顶点 $i$ 的顶点的二维数组表示图称为邻接矩阵表示。
这是大多数图形问题的输入,所以让我们记住它。

1-4 问题 C 上一个排列

问题

给定 $(1,2,...,N)$ 的排列 $P$。
当 $(1,2,...,N)$ 的所有排列按字典顺序排列时,输​​出 $P$ 之前的排列。
保证存在 $(1,2,...,N)$ 的排列,按字典顺序排列在 $P$ 之前。

约束

・$1 leq N leq 100$

评论

天真地列举 $(1,2,...,N)$ 的排列需要 $O(N!)$,考虑到我们的限制,这是不计后果的。
让我们考虑如何从给定的排列 $P$ 中找到所需的排列 $Q$。

考虑重新排列 $P$ 以产生 $Q$。
最好尽量减少 $P$ 的向左变化。
如果要改变右边的$n$项,只有当$P$右边的$n$项单调递增时,才需要改变$P$右边的$n$项。
以输入/输出例子2为例,$A=(9,8,6,5,10,3,1,2,4,7)$的前一个排列是$B=(9,8, 6, 5,10,2,7,4,3,1)$。
由于$(1,2,4,7)$是单调递增的,即使重新排列也不能按字典序变小,可以像$(2,7,4,3,1)$那样按字典序变小。

ABC276をPythonとC++で

并且如上图所示,$P$右边的$n$项中最左边的$3$是$(1,2,3,4,7)$中的$3$之后的第二小。它变为$2$,单调递增的部分单调递减。 .

换句话说,您应该实现这一点。
(1) 找到$i$ 项从$A$ 的右侧单调递增的最大$i$,并将其设为$n$。
(2)在$A$右边的$n$个项目中,找到$A$右边的$n$个项目之后的下一个最小的数字,并将其索引设置为$m$。
(3)从$A$的右边交换$n$项和$A_m$,从右边反转$n-1$项。

所有这些操作都可以在$O(N)$中完成,因此总计算复杂度为$O(N log N)$。

实现“右边的 $i$ 项”使实现复杂化。

Python中的实现示例
N=int(input())
P=list(map(int,input().split()))

n=N-2 #P[n]より右は単調増加
while P[n]<P[n+1]:
  n-=1

m=N-1 #P[n]より右にある、P[n]未満の最小
while P[n]<P[m]: 
  m-=1

P[n],P[m]=P[m],P[n] #P[n],P[m]を交換
ans=P[:n+1]+P[n+1:][::-1] #P[n]より左はそのまま、P[n]以右は反転させる
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;
  vector<int> P(N);
  rep(i,0,N) cin>>P[i];
  
  int n=N-2;
  while(P[n]<P[n+1]){
    n--;
  }
  
  int m=N-1;
  while(P[n]<P[m]){
    m--;
  }
  
  swap(P[n],P[m]);
  reverse(P.begin()+n+1,P.end());
  rep(i,0,N){
    cout<<P[i];
    if(i==N-1){
      cout<<endl;
    }else{
      cout<<" ";
    }
  }
}

它已经完全向后兼容官方评论......

作为旁注

似乎 C++ 在标准库中有一个名为 prev_permutation 的类似作弊的函数,它按字典顺序返回先前的排列。我生气

C++ 中的实现示例(`prev_permutation`)
#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> P(N);
  rep(i,0,N) cin>>P[i];
  
  prev_permutation(P.begin(),P.end());
  rep(i,0,N){
    cout<<P[i];
    if(i==N-1){
      cout<<endl;
    }else{
      cout<<" ";
    }
  }
}

1-5 D 题 除以 2 或 3

问题

给定一个长度为 $N$ 的整数序列 $A$。
您可以多次执行以下操作。

(1) 选择一个$A_i$,它是$2$ 的倍数,并将其替换为$ frac {A_i}{2}$。
②选择一个$A_i$,它是$3$ 的倍数,并替换为$ frac {A_i}{3}$。

我希望这个操作能够均衡 $A$ 的所有元素。
找出为此目的所需的最少操作数。

约束

评论

当所有$A$ 相等时,$A_i$ 的值是$A$ 的公约数。
并且$A_i$在运算时成为$A$的最大公约数,从而使运算次数最小化。

因此,您应该执行以下操作:
① 求$A$ 的最大公约数$g$。
② 对于每个 $ frac{A_i}{g}$,求它除以 $2$ 或 $3$ 的次数得到 $1$。这些值的总和就是答案。如果甚至有一件事不能是 $1$,那么答案就是 $-1$。

第①部分的计算复杂度为$O(N log max A)$。

最大公约数的复杂度

在这里,我们将解释找到 $2$ 自然数 $A,B$ 的最大公约数的计算量。
目前已知的求最大公约数的最有效方法是欧几里得除法。
随着我们继续进行欧几里德除法,两个数字 $A 和 B$ 会呈指数级变小。
当执行 $2$ 步骤时,$A 和 B$ 总是小于彼此的一半。
所以复杂度是$O(log min (A,B))$。

第 2 部分是 $O(N log max A)$,因为我们将每个 $frac{A_i}{g}$ 除以 $2$ 或 $3$。

因此,总计算复杂度为$O(N log max A)$。

Python中的实现示例
from math import gcd

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

g=0
for i in range(N):
  g=gcd(g,A[i])

ans=0
for i in range(N):
  now=A[i]//g
  while now%2==0:
    now//=2
    ans+=1
  while now%3==0:
    now//=3
    ans+=1
  if now!=1:
    print(-1); exit()
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;
  vector<int> A(N);
  rep(i,0,N) cin>>A[i];
  
  int g=0;
  rep(i,0,N){
    g=gcd(g,A[i]);
  }
  
  int ans=0;
  rep(i,0,N){
    int now=A[i]/g;
    while(now%2==0){
      now/=2; ans++;
    }
    while(now%3==0){
      now/=3; ans++;
    }
    if(now!=1){
      cout<<-1<<endl; return 0;
    }
  }
  cout<<ans<<endl;
}

1-6 E 问题往返

问题

有$H×W$个正方形,从上数第$i$行和从左数第$j$列的正方形表示为$(i,j)$。
每个方格要么是起点(S),要么是道路(.),要么是障碍物(#),不能去有障碍物的方格。
从起点出发,上下左右,返回起点,判断是否有一条长度为$4$以上的路径除了第一个和最后一个不经过同一个方格。 .

约束

・$2 leq H, W $
・$4 leq H×W leq 10^6$

评论

首先,让我们考虑一下“路线”。
路线为起点→起点相邻的道路$A$→起点相邻的道路$B$→返回起点。
这里,对于与起点相邻的任意路径$A和B$,如果$A和B$可以在起点以外的地方进出,那么总会有一条长度为$4$或更长的路径。

证明

“起点→起点和相邻路径$A$”和“起点和相邻路径$B$→起点”的长度都是$1$。
如果您可以从 $A$ 到 $B$,则与起点相邻的任何道路 $A 和 B$ 必须是 $2$ 或更多。因为$A 和B$ 不能相邻。
因此,“路径”长度大于$1+1+2=4$。

这使得解决决策问题变得容易。

对于 $(A,B)$(最多 $6$)的所有可能路径对,检查 $A$ 和 $B$ 是否经过起点以外的路径就足够了。可以使用以下方法回答

BFS的计算量为$O(HW)$,UnionFind的计算量为$O(HW)$,因为查询基本上可以在$O(1)$中处理。 .

实现是在 BFS 中完成的。

Python中的实现示例
from collections import deque

H,W=map(int,input().split())
C=[list(input()) for _ in range(H)]

sx,sy=0,0
for i in range(H):
  for j in range(W):
    if C[i][j]=="S":
      sx,sy=i,j

dir=[[0,-1],[0,1],[-1,0],[1,0]]
ok=lambda x,y:0<=x<H and 0<=y<W and C[x][y]=="."

def BFS(X,Y):
  D=deque(); D.append((X,Y))
  dist=[[-1]*W for _ in range(H)]; dist[X][Y]=0
  while D:
    x,y=D.popleft()
    for a,b in dir:
      nx,ny=x+a,y+b
      if not (ok(nx,ny) and dist[nx][ny]==-1): continue
      dist[nx][ny]=dist[x][y]+1
      D.append((nx,ny))
  return dist

l=[]
for a,b in dir:
  x,y=sx+a,sy+b
  if ok(x,y):
    l.append((x,y))

for i in range(len(l)):
  x,y=l[i]
  dist=BFS(x,y)
  for j in range(i+1,len(l)):
    nx,ny=l[j]
    if dist[nx][ny]!=-1:
      print("Yes")
      exit()
print("No")
C++中的实现示例
#include <bits/stdc++.h>
using namespace std;
#define rep(i,N,M) for(int i=N; i<M; i++)

int H,W;
vector<string> C(1000000);
vector<pair<int,int>> dir={{0,-1},{0,1},{-1,0},{1,0}};

bool ok(int x,int y){
  return 0<=x and x<H and 0<=y and y<W and C[x][y]=='.';
}

vector<vector<int>> BFS(int X,int Y){
  deque<pair<int,int>> D; D.push_back(make_pair(X,Y));
  vector<vector<int>> dist(H,vector<int>(W,-1)); dist[X][Y]=0;
  
  while(!D.empty()){
    int x,y; tie(x,y)=D.front(); D.pop_front();
    rep(i,0,4){
      int a,b; tie(a,b)=dir[i];
      int nx=x+a,ny=y+b;
      if(ok(nx,ny) and dist[nx][ny]==-1){
        dist[nx][ny]=dist[x][y]+1;
        D.push_back(make_pair(nx,ny));
      }
    }
  }
  return dist;
}

int main(){
  cin>>H>>W;
  int sx=0,sy=0;
  rep(i,0,H){
    cin>>C[i];
    rep(j,0,W){
      if(C[i][j]=='S'){
        sx=i; sy=j;
      }
    }
  }
  
  vector<pair<int,int>> l;
  rep(i,0,4){
    int a,b; tie(a,b)=dir[i];
    int nx=sx+a,ny=sy+b;
    if(ok(nx,ny)){
      l.push_back(make_pair(nx,ny));
    }
  }
  
  int n=l.size();
  rep(i,0,n){
    int ax,ay; tie(ax,ay)=l[i];
    vector<vector<int>> dist=BFS(ax,ay);
    rep(j,i+1,n){
      int bx,by; tie(bx,by)=l[j];
      if(ok(bx,by) and dist[bx][by]!=-1){
        cout<<"Yes"<<endl; return 0;
      }
    }
  }
  cout<<"No"<<endl;
}
实施起来是不是太难了?

1-7 F 问题双重机会

问题

我稍后会解释。

2 最后

感谢您阅读到最后。
这段时间一定很艰难。
我很惊讶 C 几乎是灰烬 Diff。
就这样。
非常感谢。
有一个良好的职业竞争生活!
请喜欢...


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

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

相关文章: