lowlinkの実装

lowlinkの実装


lowlinkというのは、例えば連結なグラフがあった場合、どこの頂点やどこの辺をなくすと連結じゃなくなるかを確認する方法です。
なくなると連結じゃなくなる頂点を関節、辺を橋といいます。

備忘録。

まず実装

int lowlink(int n,int m, vector<vector<int>>rotti)
{
  //与えられるグラフは連結なグラフが条件
  vector<int>stack{1};
  vector<int>num(n,-1),low(n,-1);
  int count=1,hako,hako2,leng=1,check;
  num[0]=0;
  low[0]=0;
  hako2=1000000000;
  while (leng>0){
    hako=stack[leng-1];
    check=0;
    for (int i=0;i<rotti[hako-1].size();i++){
      if (num[rotti[hako-1][i]-1]!=-1){
	if (stack[max(leng-2,0)]!=rotti[hako-1][i])hako2=min(hako2,low[rotti[hako-1][i]-1]);
	continue;
      }
      stack.emplace_back(rotti[hako-1][i]);
      num[rotti[hako-1][i]-1]=count;
      low[rotti[hako-1][i]-1]=count;
      count++;
      leng++;
      hako2=1000000000;
      check=1;
      break;
    }
    if (check==1)continue;
    low[hako-1]=min(low[hako-1],hako2);
    low[stack[max(0,leng-2)]-1]=min(low[stack[max(0,leng-2)]-1],low[hako-1]);
    stack.pop_back();
    leng--;
  }
  /*
  グループ分けの結果を出力
  for (int i=0;i<low.size();i++)cout << low[i] << " ";
  cout << endl;
  */
  //関節の個数の出力
  set<int>irane;
  int ans=0;
  for (int i=0;i<n;i++){
    irane.clear();
    for (int ipp=0;ipp<rotti[i].size();ipp++)irane.insert(low[rotti[i][ipp]-1]);
    if (irane.size()>1)ans++;
  }
  cout << ans << endl;
  return 0;
}

引数に、頂点の個数N、辺の数M、連結関係を格納した二次元グラフを入れます。


どういうことをしているのかというと、グラフのそれぞれの頂点をループしているところでグループわけみたいにして、それぞれの頂点にグループの番号を降り、2つ以上のグループを連結している頂点や辺は関節や橋だとするということです。

図で解説します。

まず、以下のようなグラフがあるとします。

最初に、DFSなどでスタートした地点から順番に番号をつけていきましょう。今回は左上からスタートすることにします。
以下のようになったとします。

このグラフでループしてるのは以下の2つです

よって2つのグループに分けます。例えば1と2にしてみます。

それぞれの頂点にグループの番号を書いてみます。

2つ以上のグループを結んでいる辺やその辺とつながっている頂点が橋や関節になります(←理由説明するのめんどくさい><)。

こういうことです(?)。
これを実装したのがさっきのコードになります。今は関節の個数を出力するようになっていますが、下に出力するコードをつけたすだけで橋の個数も、関節になっている頂点の番号も出力することができます。(頂点の番号を格納しているのがnum,グループの番号を格納しているのがlow)。


例えば下のコードなどで受け取ったグラフの関節の個数を出力できます。

#include <bits/stdc++.h>
using namespace std;
int lowlink(int n,int m, vector<vector<int>>rotti)
{
  //与えられるグラフは連結なグラフが条件
  vector<int>stack{1};
  vector<int>num(n,-1),low(n,-1);
  int count=1,hako,hako2,leng=1,check;
  num[0]=0;
  low[0]=0;
  hako2=1000000000;
  while (leng>0){
    hako=stack[leng-1];
    check=0;
    for (int i=0;i<rotti[hako-1].size();i++){
      if (num[rotti[hako-1][i]-1]!=-1){
	if (stack[max(leng-2,0)]!=rotti[hako-1][i])hako2=min(hako2,low[rotti[hako-1][i]-1]);
	continue;
      }
      stack.emplace_back(rotti[hako-1][i]);
      num[rotti[hako-1][i]-1]=count;
      low[rotti[hako-1][i]-1]=count;
      count++;
      leng++;
      hako2=1000000000;
      check=1;
      break;
    }
    if (check==1)continue;
    low[hako-1]=min(low[hako-1],hako2);
    low[stack[max(0,leng-2)]-1]=min(low[stack[max(0,leng-2)]-1],low[hako-1]);
    stack.pop_back();
    leng--;
  }
  /*
  グループ分けの結果を出力
  for (int i=0;i<low.size();i++)cout << low[i] << " ";
  cout << endl;
  */
  //関節の個数の出力
  set<int>irane;
  int ans=0;
  for (int i=0;i<n;i++){
    irane.clear();
    for (int ipp=0;ipp<rotti[i].size();ipp++)irane.insert(low[rotti[i][ipp]-1]);
    if (irane.size()>1)ans++;
  }
  cout << ans << endl;
  return 0;
}



int main()
{
  int n,m,a,b;
  cin >> n >> m;
  vector<vector<int>>rotti(n);
  for (int i=0;i<m;i++){
    cin >> a >> b;
    rotti[a-1].emplace_back(b);
    rotti[b-1].emplace_back(a);
  }
  lowlink(n,m,rotti);
}

たぶん見る人いないと思うので適当に書きました。あとで丁寧に書く。
あと戻り値をゼロにして関数の中で出力しているのは軽く確認のためです。書き直さなきゃ…

ABC335 A~E問題

ABC335


atcoder.jp



A問題 2023

Pythonだとスライスが便利です

s=list(input())
for i in range(len(s)-1):
  print(s[i],end='')
print("4")
#include <bits/stdc++.h>
using namespace std;
int main()
{
  string s;
  cin >> s;
  for (int i=0;i<s.size()-1;i++)cout << s[i];
  cout << 4 << endl;
  return 0;
}

B問題 Tetrahedral Number

全探索で通ります。

n=int(input())
for i in range(n+1):
  for ipp in range(n+1):
    for k in range(n+1):
      if i+ipp+k<=n:
        print(i,ipp,k)
#include <bits/stdc++.h>
using namespace std;
int main()
{
  int n;
  cin >> n;
  for (int a=0;a<=n;a++){
    for (int b=0;b<=n;b++){
      for (int c=0;c<=n;c++){
	if (a+b+c<=n)cout << a << " " << b << " " << c << endl;
      }
    }
  }
  return 0;
}

C問題 Loong Tracking

すべての座標をリストに入れて添字をいい感じにする

n,q=map(int,input().split())
rotti=[]
for i in range(n):
  rotti.append([n-i,0])
count=0
for i in range(q):
  a,b=input().split()
  if a=="1":
    x=rotti[len(rotti)-1][0]
    y=rotti[len(rotti)-1][1]
    if b=="R":
      rotti.append([x+1,y])
    elif b=="L":
      rotti.append([x-1,y])
    elif b=="U":
      rotti.append([x,y+1])
    else:
      rotti.append([x,y-1])
    count+=1
  else:
    print(rotti[n-int(b)+count][0],rotti[n-int(b)+count][1])

D問題 Loong and Takahashi

一番外側から順番にまいていくようにすれば簡単です。

n=int(input())
rotti=[[-1]*n for i in range(n)]
rotti[n//2][n//2]="T"
hako=["R","D","L","U"]
iti=0
a=0
b=0
for i in range(n*n-1):
  rotti[a][b]=i+1
  if hako[iti]=="R" and b+1<n and rotti[a][b+1]==-1:
    b+=1
    continue
  elif hako[iti]=="D" and a+1<n and rotti[a+1][b]==-1:
    a+=1
    continue
  elif hako[iti]=="L" and b-1>=0 and rotti[a][b-1]==-1:
    b-=1
    continue
  elif hako[iti]=="U" and a-1>=0 and rotti[a-1][b]==-1:
    a-=1
    continue
  else:
    iti+=1
    if len(hako)==iti:
      iti=0
  if hako[iti]=="R" and b+1<n:
    b+=1
    continue
  elif hako[iti]=="D" and a+1<n:
    a+=1
    continue
  elif hako[iti]=="L" and b-1>=0:
    b-=1
    continue
  elif hako[iti]=="U" and a-1>=0:
    a-=1
    continue
for i in range(n):
  print(*rotti[i])

E問題 Non-Decreasing Colorful Path

そもそもこの問題だと、書いてある整数が大きい頂点から小さい頂点に行くことは絶対にできないので最初に辺を削除します(値が小さい頂点から大きい頂点しか行けない有向グラフにする)

その後、その頂点に来るまでに何通りの方法があるかDPします。

ここで、同じ数字かつ連結関係にある頂点たちは、まとめて一つの頂点ということにします。

n,m=map(int,input().split())
amount=list(map(int,input().split()))
rotti=[[]*1 for i in range(n)]
same=[[]*1 for i in range(n)]
irane=set()
for i in range(m):
  a,b=map(int,input().split())
  if amount[a-1]==amount[b-1]:
    same[a-1].append(b)
    same[b-1].append(a)
    irane.add(a)
    irane.add(b)
  elif amount[a-1]>amount[b-1]:
    rotti[b-1].append(a)
  else:
    rotti[a-1].append(b)

irane_rotti=set([1])
iti=0
stack=[1]
while len(stack)>iti:
  hako=stack[iti]
  for i in range(len(rotti[hako-1])):
    if rotti[hako-1][i] in irane_rotti:
      continue
    irane_rotti.add(rotti[hako-1][i])
    stack.append(rotti[hako-1][i])
  for i in range(len(same[hako-1])):
    if same[hako-1][i] in irane_rotti:
      continue
    irane_rotti.add(same[hako-1][i])
    stack.append(same[hako-1][i])
  iti+=1

dp=[0]*n
dp[0]=1
stack=[]
a=[]
hako=amount[0]
for i in range(len(amount)):
  if not i+1 in irane_rotti:
    continue
  a.append([amount[i],(i+1)*-1])
a=sorted(a)
for i in range(len(a)):
  stack.append(a[i][1]*-1)
iti=0
irane_num=set()
while len(stack)>iti:
  hako=stack[iti]
  if not hako in irane:
    for i in range(len(rotti[hako-1])):
      dp[rotti[hako-1][i]-1]=max(dp[rotti[hako-1][i]-1],dp[hako-1]+1)
    iti+=1
    continue

  if hako in irane_num:
    iti+=1
    continue
  irane_num.add(hako)
  
  hako2=[hako]
  iti2=0
  irane2=set([hako])
  while len(hako2)>iti2:
    hako=hako2[iti2]
    for i in range(len(same[hako-1])):
      if same[hako-1][i] in irane2:
        continue
      hako2.append(same[hako-1][i])
      irane_num.add(same[hako-1][i])
      irane2.add(same[hako-1][i])
    iti2+=1
  irane2=set()
  count=0
  for i in range(len(hako2)):
    hako=hako2[i]
    count=max(count,dp[hako-1])
    for ipp in range(len(rotti[hako-1])):
      irane2.add(rotti[hako-1][ipp])
      #dp[rotti[hako-1][i]-1]=dp[hako-1]+1
      #stack.append(rotti[hako-1][i])
  for i in range(len(hako2)):
    hako=hako2[i]
    dp[hako-1]=count
  irane2=list(irane2)
  for i in range(len(irane2)):
    hako=irane2[i]
    dp[hako-1]=max(dp[hako-1],count+1)
  iti+=1
print(dp[n-1])


rotti-coder.hatenablog.com

ABC334 A~E問題

ABC334


atcoder.jp


A問題 Christmas Presen

条件分岐

a,b=map(int,input().split())
if a>b:
  print("Bat")
else:
  print("Glove")

B問題 Christmas Trees

lとrが負の整数か、それとも正の整数かで3つに場合分けして、いい感じに割り算をします。
条件分岐の内容としては、lとrがどちらも負の整数の場合、片方が負の整数でもう片方が正の整数の場合、2つとも正の整数の場合の3つに場合分けします。

a,m,l,r=map(int,input().split())
l-=a
r-=a
if l<=0 and 0<=r:
  hako=1
else:
  hako=0
if l>=0 and r>=0:
  print(r//m-(l-1)//m+hako)
elif l<0 and r>=0:
  print(r//m+abs(l)//m+hako)
else:
  print(abs(l)//m-(abs(r)-1)//m+hako)

C問題 Socks 2

まず、この問題は、持っている靴下の合計が偶数か奇数かで場合分けします。

もし偶数の場合は全ての靴下を使うことになり、愚直に靴下の色をソートして、前から2つずつをペアにしていけばよいです。

もし奇数の場合は少し面倒なことになります。基本的には偶数のときと同じように、ソートして前から2つずつをペアにしていく…ということをすればいいのですが、奇数の場合は一枚あまることになります。その余る一枚を決めるのが少々めんどくさいところです。


ぼくは、前と後ろから累積和をとり、いい感じにやりました。こうすることで、その靴下を使わないなら奇妙さの総和はいくつになるか…が一回の計算で求まります。

全ての靴下に対してこの計算をすれば答えが出ます。

n,k=map(int,input().split())
a=set(list(map(int,input().split())))
hako=(2*n-k)//2
rotti=[]
for i in range(n):
  if i+1 in a:
    rotti.append(i+1)
  else:
    hako-=1
if len(rotti)%2==0:
  ans=0
  for i in range(len(rotti)//2):
    ans+=abs(rotti[i*2]-rotti[i*2+1])
  print(ans)
  exit()
a=[0]
b=[0]
for i in range(len(rotti)//2):
  a.append(a[i]+abs(rotti[i*2]-rotti[i*2+1]))
for i in range(len(rotti)//2):
  b.append(b[i]+abs(rotti[len(rotti)-1-i*2]-rotti[len(rotti)-1-i*2-1]))
ans=1000000000
for i in range(len(a)):
  ans=min(a[i]+b[len(b)-1-i],ans)
print(ans)

D問題 Reindeer and Sleigh

与えられた配列Rを累積和にします。
そして、クエリが与えられるたびに二分探索で何匹目まで運べるかを求めます。

import bisect
n,q=map(int,input().split())
amount=sorted(list(map(int,input().split())))
rotti=[0]
for i in range(len(amount)):
  rotti.append(rotti[i]+amount[i])
for i in range(q):
  a=int(input())
  hako=bisect.bisect(rotti,a)
  if hako==len(rotti):
    print(len(rotti)-1)
  elif rotti[hako]==a:
    print(hako)
  else:
    print(hako-1)

E問題 Christmas Color Grid 1

制約をみる限り、全てのマスに対して”#”をおいたときにグリッドの緑の連結成分数がいくつになるかを求めても実行時間は大丈夫そうなので全探索します。

h,w=map(int,input().split())
rotti=[[]*1 for i in range(h)]
for i in range(h):
  rotti[i]=list(input())
rotti2=[[-1]*w for i in range(h)]
done=[[0]*w for i in range(h)]
count=0
for i in range(h):
  for ipp in range(w):
    if done[i][ipp]==1 or rotti[i][ipp]==".":
      continue
    done[i][ipp]=1
    stack=[[i,ipp]]
    iti=0
    count+=1
    while len(stack)>iti:
      a=stack[iti][0]
      b=stack[iti][1]
      rotti2[a][b]=count
      #ue
      if a-1>=0 and done[a-1][b]==0 and rotti[a-1][b]=="#":
        stack.append([a-1,b])
        done[a-1][b]=1
      #migi
      if b+1<w and done[a][b+1]==0 and rotti[a][b+1]=="#":
        stack.append([a,b+1])
        done[a][b+1]=1
      #sita
      if a+1<h and done[a+1][b]==0 and rotti[a+1][b]=="#":
        stack.append([a+1,b])
        done[a+1][b]=1
      #hidari
      if b-1>=0 and rotti[a][b-1]=="#" and done[a][b-1]==0:
        stack.append([a,b-1])
        done[a][b-1]=1
      iti+=1

q=0
p=0
for i in range(h):
  for ipp in range(w):
    if rotti[i][ipp]=="#":
      continue
    q+=1
    hako=[]
    #ue
    if i-1>=0 and rotti[i-1][ipp]=="#":
      hako.append(rotti2[i-1][ipp])
    #migi
    if ipp+1<w and rotti[i][ipp+1]=="#":
      hako.append(rotti2[i][ipp+1])
    #sita
    if i+1<h and rotti[i+1][ipp]=="#":
      hako.append(rotti2[i+1][ipp])
    #hidari
    if ipp-1>=0 and rotti[i][ipp-1]=="#":
      hako.append(rotti2[i][ipp-1])
    hako=set(hako)
    if len(hako)==1:
      p+=count
    elif len(hako)==0:
      p+=count+1
    elif len(hako)==2:
      p+=count-1
    elif len(hako)==3:
      p+=count-2
    elif len(hako)==4:
      p+=count-3


def kouyaku(a,b):
  while a>0 and b>0:
    if a<b:
      b=b%a
    elif a==b:
      return a
    else:
      a=a%b
  return max(a,b)
def koubai(a,b):
  return a*b//kouyaku(a,b)
count=kouyaku(p,q)
p=p//count
q=q//count


for i in range(1000000):
  p+=998244353
  if p%q==0:
    print((p//q)%998244353)
    break

rotti-coder.hatenablog.com

ABC333 A~E問題

ABC333

atcoder.jp

A問題 Three Threes

愚直に実行!

python

n=int(input())
for i in range(n):
  print(str(n),end='')
print()

B問題 Pentagon

気合で条件分岐しました。
Twitterなどで見かけましたが、いい感じの文字列を作り与えられた入力が部分文字列化どうかで判定するスマートなやり方もあるそうですね。

Python

a=list(input())
a=sorted(a)
hako=""
for i in range(2):
  hako+=a[i]
a=0
if hako=="AB":
  a=1
elif hako=="AE":
  a=1
elif hako=="AC":
  a=2
elif hako=="AD":
  a=2
elif hako=="BC":
  a=1
elif hako=="BD" or hako=="BE":
  a=2
elif hako=="CD":
  a=1
elif hako=="CE":
  a=2
elif hako=="DE":
  a=1
b=list(input())
b=sorted(b)
hako=""
for i in range(2):
  hako+=b[i]
b=a
a=0
if hako=="AB":
  a=1
elif hako=="AE":
  a=1
elif hako=="AC":
  a=2
elif hako=="AD":
  a=2
elif hako=="BC":
  a=1
elif hako=="BD" or hako=="BE":
  a=2
elif hako=="CD":
  a=1
elif hako=="CE":
  a=2
elif hako=="DE":
  a=1

if a==b:
  print("Yes")
else:
  print("No")

C問題 Repunit Trio

全列挙が間に合う制約です。

rotti=[]
count=1
for i in range(100):
  rotti.append(count)
  count*=10
  count+=1

hako=[]
for i in range(100):
  for ipp in range(100):
    hako.append(rotti[i]+rotti[ipp])
hako2=[]
for i in range(len(hako)):
  for ipp in range(len(rotti)):
    hako2.append(hako[i]+rotti[ipp])
hako2=sorted(list(set(hako2)))
n=int(input())
print(hako2[n-1])

D問題 Erase Leaves

1から出ている木それぞれをまるごと消すのに何回操作する必要があるかを計算し、一番操作が必要な木ひとつを除き、全て削除すればいいです。

何回操作する必要があるかを求めるには、端の葉からDPを行えばよいです。

n=int(input())
rotti=[[]*1 for i in range(n)]
for i in range(n-1):
  a,b=map(int,input().split())
  rotti[a-1].append(b)
  rotti[b-1].append(a)
if len(rotti[0])==1:
  print(1)
  exit()
iti=0
amount=[[1,0]*1 for i in range(n)]
stack=[]
irane=set()
ans=[]
for i in range(1,n):
  if len(rotti[i])==1:
    stack.append(i+1)
    irane.add(i+1)
while len(stack)>iti:
  hako=stack[iti]
  if hako==1:
    iti+=1
    continue
  for i in range(len(rotti[hako-1])):
    if rotti[hako-1][i] in irane:
      continue
    if rotti[hako-1][i]==1:
      ans.append(amount[hako-1][0])
      continue
    amount[rotti[hako-1][i]-1][0]+=amount[hako-1][0]
    amount[rotti[hako-1][i]-1][1]+=1
    if amount[rotti[hako-1][i]-1][1]==len(rotti[rotti[hako-1][i]-1])-1 and rotti[hako-1][i]!=1:
      stack.append(rotti[hako-1][i])
      irane.add(rotti[hako-1][i])
  iti+=1
ans=sorted(ans)
print(sum(ans)-ans[len(ans)-1]+1)

E問題 Takahashi Quest

先に入力をすべて受けとり、ポーションとモンスターで分けてそれぞれのタイプごとにいつ発生するかを配列などにまとめておきます。

一度に持つ量の最大値を最小化するのが目標なので、できるだけ一つのポーションをもっている時間を短くしたいです。ですから、タイプXのモンスターが発生する時、それよりも前の中で一番最近でポーションを入手できるときに入手するようにします。

このような貪欲で解くことができます。

import bisect
n=int(input())
potion=[[]*1 for i in range(n)]
monster=[[]*1 for i in range(n)]
hako2=[]
for i in range(n):
  a,b=map(int,input().split())
  if a==1:
    potion[b-1].append(i)
    hako2.append(i+1)
  else:
    monster[b-1].append(i)
check=0
ans=[]
for i in range(n):
  if len(monster[i])==0:
    continue
  if len(monster[i])>len(potion[i]):
    check=1
    break
  a=len(monster[i])-1
  hako=len(potion[i])
  for ipp in range(len(potion[i])):
    if potion[i][hako-ipp-1]<monster[i][a]:
      ans.append([potion[i][hako-ipp-1],1])
      ans.append([monster[i][a],2])
      a-=1
      if a==-1:
        break
  if a>=0:
    check=1
    break
if check==1:
  print(-1)
  exit()
ans=sorted(ans)
count=0
rotti=0
for i in range(len(ans)):
  if ans[i][1]==1:
    count+=1
    rotti=max(rotti,count)
  else:
    count-=1
print(rotti)
rotti=set()
for i in range(len(ans)):
  if ans[i][1]==1:
    rotti.add(ans[i][0]+1)
ans=[]
for i in range(len(hako2)):
  if hako2[i] in rotti:
    ans.append(1)
  else:
    ans.append(0)
print(*ans)

ちなみに二分探索を使いました。


rotti-coder.hatenablog.com

ABC332 A~D問題

ABC332


atcoder.jp


A問題 Online Shopping

合計金額を出し、送料がどうなるかを確認して出力すればよいです。

c++

#include <bits/stdc++.h>
using namespace std;
int main()
{
  int n,s,k,a,b;
  int count=0;
  cin >> n >> s >> k;
  for (int i=0;i<n;i++){
    cin >> a >> b;
    count+=a*b;
  }
  if (count>=s)cout << count << endl;
  else cout << count+k << endl;
  return 0;
}

python

n,s,k=map(int,input().split())
amount=0
for i in range(n):
  a,b=map(int,input().split())
  amount+=a*b
if amount>=s:
  print(0+amount)
else:
  print(k+amount)

B問題 Glass and Mug

単純に、問題にかかれていることを愚直に実行するコードで通ります。

c++

#include <bits/stdc++.h>
using namespace std;
int main()
{
  int k,g,m,count;
  int a=0;
  int b=0;
  cin >> k >> g >> m;
  for (int i=0;i<k;++i){
    if (a==g)a=0;
    else if (b==0)b=m;
    else{
      count=min(g-a,b);
      b-=count;
      a+=count;
    }
  }
  cout << a << " " << b << endl;
  return 0;
}

python

k,g,m=map(int,input().split())
a=0
b=0
for i in range(k):
  if a==g:
    a=0
  elif b==0:
    b=m
  else:
    count=min(g-a,b)
    b-=count
    a+=count
print(a,b)

C問題 T-shirts

ロゴ入りのTシャツは食事に行く時と競技プログラミングのイベントに行くときのどちらも使えるのに対して、無地のTシャツは食事に行く時しか使えないので、無地のTシャツを優先して使います。

持っているTシャツを使える限り使えるように運用して、足りなくなったら一枚購入するようにし、最終的に何枚必要になるか計算して出力すればいいです。

python

n,m=map(int,input().split())
s=list(input())
rotti=[]
hako=""
for i in range(n):
  if s[i]=="0" and len(hako)>0:
    rotti.append(hako)
    hako=""
  else:
    hako+=s[i]
rotti.append(hako)
A=0
B=0
for i in range(len(rotti)):
  hako=list(rotti[i])
  if hako=="":
    continue
  a=0
  b=0
  for ipp in range(len(hako)):
    if hako[ipp]=="2":
      a+=1
    elif hako[ipp]=="1":
      b+=1
  A=max(A,a)
  B=max(B,b+a)
print(A+max(0,B-m-A))

D問題 Swapping Puzzle

いろいろな解き方があると思いますが、ぼくはグリッドの状態をBFSしていく解き方で解きました。

BFSの時、二次元配列などをそのまま使うときは、メモリの同じIDを共有しないように気をつける必要があります←Pythonは。

import copy
h,w=map(int,input().split())
a=[[]*1 for i in range(h)]
B=[[]*1 for i in range(h)]
for i in range(h):
  a[i]=list(map(int,input().split()))
for i in range(h):
  B[i]=list(map(int,input().split()))
stack=[]
stack.append([copy.deepcopy(a),0])
iti=0
count=0
hako=1
for i in range(h):
  for ipp in range(w):
    count+=a[i][ipp]*hako
    hako*=10
irane=set([count])
rotti=[[0]*w for i in range(h)]
while len(stack)>iti:
  #縦に変更
  check=0
  rotti=copy.deepcopy(stack[iti][0])
  for i in range(h):
    for ipp in range(w):
      if rotti[i][ipp]!=B[i][ipp]:
        check=1
        break
  if check==0:
    print(stack[iti][1])
    break
  for i in range(h-1):
    rotti=copy.deepcopy(stack[iti][0])
    a=[]
    for ipp in range(w):
      a.append(rotti[i][ipp])
    for ipp in range(w):
      rotti[i][ipp]=rotti[i+1][ipp]
    for ipp in range(w):
      rotti[i+1][ipp]=a[ipp]
    count=0
    hako=1
    for a in range(h):
      for b in range(w):
        count+=hako*rotti[a][b]
        hako*=10
    if not count in irane:
      stack.append([copy.deepcopy(rotti),stack[iti][1]+1])
      irane.add(count)
  #横に変更

  for i in range(w-1):
    rotti=copy.deepcopy(stack[iti][0])
    a=[]
    for ipp in range(h):
      a.append(rotti[ipp][i])
    for ipp in range(h):
      rotti[ipp][i]=rotti[ipp][i+1]
    for ipp in range(h):
      rotti[ipp][i+1]=a[ipp]
    count=0
    hako=1
    for a in range(h):
      for b in range(w):
        count+=rotti[a][b]*hako
        hako*=10
    if not count in irane:
      stack.append([copy.deepcopy(rotti),stack[iti][1]+1])
      irane.add(count)
 
  iti+=1
else:
  print(-1)


rotti-coder.hatenablog.com

ABC329 A~D、F問題

ABC329 A~D,F

A問題 Spread

これはいい感じにやるだけです。


自分のPythonのコード

s=list(input())
print(s[0],end='')
for i in range(1,len(s)):
  print(f" {s[i]}",end='')

B問題 Next

うーんこれもやるだけですね。
ぼくは、数列Aをリストで受け取った後、Set型に変換して、重複をなくし、その後またリストにもどしてからソートし、上から二番目の数字を出力させることにしました。

Pythonの自分のコード

n=int(input())
a=sorted(list(set(list(map(int,input().split())))))
print(a[len(a)-2])

C問題 Count xxx

これは、一種類の文字からなる部分文字列を全て格納するSet型などを作って、全部入れた後重複がない要素数を出力すれば答えが出ます

…しかし、この方法だとSet型に格納するために部分文字列を格納しておく変数が大変なことになります(語彙力消滅)

マジで説明下手くそなんですけど、例えばPythonなどを使う時、まずaが一個続いてたら変数sにaを追加しよう。で、それをSet型にぶち込む
またaが続いてたらsにaを接続してあげてまたそれをSet型にぶちこむ…

みたいな方法が思いつくかも知れませんが、Pythonの文字列型の連結には確かO(N)かかるはずなのでaがたくさんつながっているときなどはTLEになってしまうんです。

…つたわったかどうかよくわからないのですが、とにかく安直な実装だとTLEになるわけです。

ぼくはその部分文字列を構成する文字に個数の数字を文字列型に変換したものを連結してSet型にぶちこむことにしました。これで解決です!(た、たぶん…)


"aaa"
→"a3"

"ccccc"
→"c5"


自分のPythonのコード

n=int(input())
s=list(input())
irane=set()
hako=s[0]
count=1
for i in range(1,len(s)):
  if not hako+str(count) in irane:
    irane.add(hako+str(count))
  if hako[0]==s[i]:
    count+=1
  else:
    hako=s[i]
    count=1
irane.add(hako+str(count))
print(len(irane))

D問題 Election Quick Report

それぞれの人の得票数を格納する配列をつくります。
で、最初に一番得票数が多い人の番号と、個数を変数に格納しておきます。

それで投票された人の番号を最初から見ていって、それぞれの人の得票数を格納する配列を更新していきます。

一回更新するたびに、更新した人の得票数がさっきまで一番得票数が多い人よりも多いかを確認して、もし多くなっていたら一番得票数が多い人を更新します。そして、更新した場合も更新しなかった場合も一番得票数が多い人を格納した変数を毎回出力します。

これでAC

自分のPythonのコード

n,m=map(int,input().split())
a=list(map(int,input().split()))
amount=[0]*n
saidai=1
for i in range(m):
  hako=a[i]
  amount[hako-1]+=1
  if amount[hako-1]>amount[saidai-1]:
    saidai=hako
  elif amount[hako-1]==amount[saidai-1] and hako<saidai:
    saidai=hako
  print(saidai)

E問題

あの、コンテスト中に解けませんでした。
たぶん最初に文字列Tと一致している文字列Sの部分を確認して、そこからBFSみたいにしていけばいいんじゃないすかね。(解いてない)

F問題 Colored Ball

マージテクっていうやつを使うみたいです。

簡単に説明すると、箱aから箱bにボールを移すときに、箱に入っているボールの数が少ない方から多い方に移すということです。こうすることによって計算量が減ります(ボールの移動する数が減るから)。

そして、そのようにした時にもし箱aから箱bにボールを移すはずが、箱bから箱aにうつしてしまった…となったら、次から箱aは箱bのことね!箱bは箱aのことね!みたいにすればおけです。

Pythonの自分のACコード

n,q=map(int,input().split())
a=list(map(int,input().split()))
count=0
irane=set()
irane2=set()
num=[0]*n
for i in range(n):
  if not a[i] in irane:
    irane.add(a[i])
  else:
    irane2.add(a[i])
    count+=1
    num[a[i]-1]+=1
amount=[[]*1 for i in range(n)]
for i in range(n):
  if a[i] in irane2:
    amount[i].append(0)
    amount[i].append(set([a[i]]))
  else:
    amount[i].append(1)
    amount[i].append(set())
check=0
for i in range(q):
  a,b=map(int,input().split())
  hako=len(amount[a-1][1])+len(amount[b-1][1])
  amount[b-1][0]+=amount[a-1][0]
  amount[a-1][0]=0

  if len(amount[a-1][1])==len(amount[b-1][1]):
    x=amount[a-1][1]
    y=amount[b-1][1]
  elif len(amount[a-1][1])<len(amount[b-1][1]):
    x=amount[a-1][1]
    y=amount[b-1][1]
  else:
    x=amount[b-1][1]
    y=amount[a-1][1]
  x=list(x)
  z=y
  for ipp in range(len(x)):
    if x[ipp] in y:
      num[x[ipp]-1]-=1
      if num[x[ipp]-1]==0:
        amount[b-1][0]+=1
        z.remove(x[ipp])
    else:
      z.add(x[ipp])
  amount[b-1][1]=z
  amount[a-1][1]=set()
  print(len(amount[b-1][1])+amount[b-1][0])
  count-=(hako-(len(amount[b-1][1])+amount[b-1][0]))
  if count==0:
    iti=i
    check=1
    break
if check==0:
  exit()
for i in range(len(amount)):
  amount[i][0]+=len(amount[i][1])
  amount[i][1]=[]
for i in range(iti+1,q):
  a,b=map(int,input().split())
  amount[b-1][0]+=amount[a-1][0]
  amount[a-1][0]=0
  print(amount[b-1][0])

rotti-coder.hatenablog.com

競プロerのための軽いUbuntu入門

本記事は競プロ Advent Calendar 2023,第 22 日目の記事として書かれました.

※「競プロアドベントカレンダー、公開されてからしばらく立つけど、まだ空いてるところがいっぱいあるなぁ…それならぼくなんかが書いた記事でものせてみていいよね?どうせ空いてるのなら!」っていう気持ちで登録してしまいました。
ぼくなんかが一枠とってごめんなさい。

土下座でチュウ

あと前半は文字ばっかりだけど後半は画像めっちゃ使って説明してるからね…!

* * *

こんにちは。ロッチです。

突然ですが、Ubuntuを使ってみたいと思ったことはありませんか?


…なんだいきなり。と思うかもしれませんが、Twitterをしている人ならUbuntuを使っている競プロerのツイートを見たことは一度はあるでしょう。
ぼくも最初はWindowsを使っていましたが、他の人のツイートに触発されてUbuntuを使い出しました。

Windowsを使っていたときは、「別にWindowsで十分じゃね?困ったこともないし」と思っていたのですが、Ubuntuに移行してからは「Windowsよりめっちゃ便利だなぁ…」ととても感じています。


もちろんWindowsにもいいところはいっぱいありますし、別にWindowsよりUbuntuの方が優れたOSだ!ということをいいたいわけではありません。どっちが使いやすいかは人それぞれだと思います。

だとしても、Windowsだけを使ってきていても、たまにUbuntuなどに興味が出たことはありませんか?
しかし、興味を持っていてもなかなか手を出しにくいのではないでしょうか。
Ubuntuを使ってみよう!と思っても、新しくOSを入れるわけですから結構面倒だったりします。

でも、せっかく使ってみたいなと思ったのに面倒だからという理由でUbuntuを使ってみないのはすごく損をしていることだとぼくは思います!

ネットで「競技プログラミング Ubuntu」と調べてみたところ、あまり思っていたような記事が出てこなかったので自分で書いてみることにしました!(調べ方が下手くそなのでちゃんと調べたら出てくると思う。これは理由をこじつk)


しかし、Ubuntuをパソコンに入れる!というところの説明は省くことにします。

ぼくにはUbuntuをパソコンに入れるのをちゃんと説明する自信はありません…

インターネットにはUbuntuをパソコンに入れる方法について説明してるサイトがめっちゃんこあるので、普通にインターネットで調べてみたほうがクオリティがいいものが見つかるはずです。インターネットにはこの手の記事はたくさん出ているでしょうから、めちゃくちゃ質のいいものはたくさんあるはずです!

※ちなみにぼくはWindowsとは違うSSDUbuntuをぶち込みデュアルブートしています。

今回ぼくが説明するのは、Ubuntuを入れたあとの環境構築のやり方などです


あと、最初に基本的な端末(コマンドライン)の操作を覚えてもらいます

端末というのは、、、これです!

端末を開いたときにはまずホームディレクトリにいます。

1 ディレクトリ(フォルダ)の作成

作りたいディレクトリを入れたいディレクトリの中で

mkdir 作りたいディレクトリ名

とコマンドを打てばよいです

ここでエンターキーを押せばおk

2 ディレクトリ間の移動

行きたいディレクトリが入っているディレクトリの中で

cd 行きたいディレクトリ名

を打てばOKです。

また、前(上位)のディレクトリに戻りたいときは

cd ..

と打ち込んであげればいいです。


ファイルの作成

ファイルを作りたいときは、作りたいファイルを入れたいディレクトリの中で

touch ファイル名

と打ち込めばよいです


ディレクトリの中身を表示

任意のディレクトリの中で、

ls

を実行すると、そのディレクトリの中に何が入っているかがわかります。

これらのコマンドが使えたら、ひとまずなんとかなります!

1 エディタ

ソースコードを書くときに絶対必要といっても過言ではないものは、エディタでしょう!

この世にエディタは星の数ほどあると思いますがその中でも主なものを3つ、vscodeEmacsVimについて入れ方を説明します!

1 Emacs

まずはEmacsの入れ方です。
Emacsの入れ方はかなり簡単です。

端末を開いて、sudo apt install emacsと打ち込めばいいだけです。

Emacsを入れる

パスワードが要求されるので、端末にパスワードを打ち込み、エンターキーをバーン!!
Emacsがパソコンの中に入ります!

Emacsの使い方は簡単で、編集したいファイルが入っているディレクトリ(フォルダ)に移動し、そこでemacs ファイル名 と打ち込むと使用することができます。

これでEmacsのウィンドウが開く

2 Vim

VimEmacsと同じように、端末を開いて sudo apt install vim と打ち込めばインストールすることができます。

使い方もEmacsと同じで、vim ファイル名 と打ち込めば、Vimで編集したいファイルを選ぶことができます。

これはさっきのもすーんバチャのコード(恥ずかしいものをごめんなさい)

…と思ったのですが、今はNeovimが多く使われているのを知っていなかったです。ぼくはViを使っていたので…
みちらからに教えられて気づきました!!
まあ、こっちも sudo apt install neovim  で入るんですけどね!

3 vscode

個人的にはEmacsVimの方がおすすめですが、VScodeにもいい点はあるし、使いたい人もいると思うので説明します!

まず、VScodeの公式サイトに移動します
https://code.visualstudio.com/Download


すると、WindowsのマークとMacのマークとペンギンさんがいると思います。
知らない人がいるかもしれないけど、ペンギンさんはLinuxのことです。

ということで、ペンギンさんのマークがあるところからダウンロードしましょう。.debと.rpmがあると思いますが、.debの方をダウンロードしてください。

こんな感じになると思います(これはVScodeというディレクトリを作って、そこにさっきの.debファイルをダウンロードした)

その後、ダウンロードしたファイルをクリックして、別のアプリケーションで開くをクリックします。

ソフトウェアのインストールというところを選択します

すると、このような画面が出てくると思うので、インストールをクリックしてください

パスワードを入れてあげましょう

インストールが始まります。しばらく待ちましょう
すると、このような画面になると思います。

開くを押してあげるとVScodeが起動します!!お気に入りにでも登録して、サイドバーなどからすぐに起動できるようにすると便利ですね


あとはWindowsとかでの使い方とおんなじ!


2 コードの実行

エディタと並んで、競技プログラミングで必要なものといえば、コードを実行することでしょう!

WindowsVScodeを使っている人は、VScodeの中でコンパイルしたり、実行したりすればいいんじゃないの…?と思っているかも知れませんが、Ubuntuでは端末でコンパイルしたり、実行したりするのが主流です(というか当たり前?)。

全部の言語について紹介するの、大変なので、PythonC++のやり方について説明します。

Python

まず、端末で sudo apt install python3 と打ち込んでください。

環境構築終わりです!

Pythonのコードを書いたら、拡張子を.pyにして保存し、端末でそのファイルがあるディレクトリに移動し、

python3 実行したいファイル名

というふうにすれば実行することができます!


C++

C++は、まずコンパイラを入れます

端末に

sudo apt install g++

と打ち込んでください

はい!コンパイラが入りました!!!!


コンパイルと実行のやり方は

まず、C++ソースコードを書いて、拡張子を.cppにして保存してください
次に、そのソースコードがおいてあるディレクトリに端末で移動します。
そして、端末に

g++ 実行したいファイル名

とぶち込んでください

これでコンパイルができました!
特に指定していなければ、そのディレクトリの中に

a.out

という実行ファイルが作成されます。

そのディレクトリの中で端末に

./a.out

などと打ち込むと、コンパイルしたC++のコードを実行することができます!

簡単でしょ?

標準入力なども端末の中でできます!
他の言語のやり方なども、ネットで調べてみたらたくさん出てくると思うので、やろうと思った人はぜひ調べてみてください〜

Chrome

UbuntuのブラウザといえばFireFox

…ですが、Chromeを使いたいという人も多いと思います。
なんかFireFoxChrome拡張機能を使うことができる!とかいう情報も聞いたことありますが、めんどくさそうだし、Chromeを使ったほうが速いなと思ったので、ぼくはChromeを入れて使ってます。

…ということでChromeの入れ方!

まず、FireFoxなどで、以下のリンクのChromeのホームページに入ってください。

https://www.google.co.jp/intl/ja/chrome/

そして、真ん中にあるChromeをダウンロードをクリックします

すると、以下のようなものが表示されると思います。

.debの方を選択して、同意してインストールをクリックしてください。
任意のディレクトリに保存した後は、VScodeを入れた時のような操作をすれば、Chromeが入ります!!

AtCoderで使う拡張機能をバンバンいれましょ〜!


これだけじゃまだまだUbuntuが使いやすくならないと思います。ぼくも、これ以外にたくさんのソフトを入れたりしました。

…でもこの記事ではそろそろ終わりにします!だって書くのが大変だから

もしUbuntuでなにかしたくなったら、すぐにインターネットで調べてください!インターネットには情報がたくさんのっています。検索エンジンなどで調べて上の方に出てくるサイト以外にも、自分の知りたいことを説明しているサイトはあるし、上の方に出てくるサイトが一番わかりやすいとは限りません。

根気よく調べていけばきっと自分の知りたいことは見つかります!頑張ってください!

楽しいUbuntuライフを!