おはやし日記

テーマ……バイク←プログラミング←旅

AtCoder ABC173 反省会 [A,B,C,D完][C++]

こんにちは。ABC173でした。

ギリチョンDまで解けました。

A

A - Payment

int main(){
  int n;cin>>n;
  for(int i=0; i<20; i++){
    if(n<=i*1000){
      cout<<(i*1000)-n<<endl;
      return 0;
    }
  }
}

なんかまどろっこしいけど通るから…………

B

B - Judge Status Summary

int main(){
  int N;cin>>N;
  int ac=0, wa=0, tle=0, re=0;
  rep(n,N){
    string s;cin>>s;
    if(s=="AC")ac++;
    if(s=="WA")wa++;
    if(s=="TLE")tle++;
    if(s=="RE")re++;
  }

  cout<<"AC x "<<ac<<endl;
  cout<<"WA x "<<wa<<endl;
  cout<<"TLE x "<<tle<<endl;
  cout<<"RE x "<<re<<endl;
}

はい。

C

C - H and V

#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for(int i=0; i<(int)n; i++)

int main(){
  int H,W,K;cin>>H>>W>>K;
  vector<string> c(H);
  rep(i,H)cin>>c[i];
  int ans=0;

  for(int i=0; i<(1<<H); i++){
    for(int j=0; j<(1<<W); j++){
      
      int cnt = 0;
      for(int h=0; h<H; h++){
        for(int w=0; w<W; w++){
          if(i&(1<<h) || j&(1<<w))continue;
          if(c[h][w]=='#')cnt++;
        }
      }

      if(cnt==K)ans++;
    }
  }

  cout<<ans<<endl;
}

制約が小さいのでbit全探索しました。ijのループで行と列それぞれ消すところを指定。

例えばi=001010(2進数)のときは「2行目と4行目を消去」を表す。

hwのループで「消去する行or列はcontinue(無視)、それ以外#があったらカウント」としています。

計算量的には $O(HW 2^{H+W})$ かな?

続きを読む

javascript(Node.js)でAtCoderを解く

Node.jsの標準入出力めんどくさすぎて半ギレになりながらもまとめました

閲覧はPC推奨です

環境

  • macOS Mojave 10.14.6
  • Node.js v12.18.1

入力

//in.txt
3 5
2.5 4
hoge

雛形

参考 : paizaプログラミングスキルチェックの値取得・出力サンプルコード | ITエンジニア向け転職・就活・学習サービス【paiza】

その1

process.stdin.resume();
process.stdin.setEncoding('utf8');

var lines = [];
var reader = require('readline').createInterface({
  input: process.stdin, output: process.stdout
});
reader.on('line', (line) => {
  lines.push(line.split(" "));
});
reader.on('close', solve);

function getInt(strArr){
  var rtn = [];
  for(elem of strArr)rtn.push(parseInt(elem));
  return rtn;
}

function getFloat(strArr){
  var rtn = [];
  for(elem of strArr)rtn.push(parseFloat(elem));
  return rtn;
}

function solve(){
  var [a,b] = getInt(lines[0]);
  var [c,d] = getFloat(lines[1]);
  var [s] = lines[2];

  console.log(a+b);
  console.log(c*d);
  console.log(s);
}
$ node test.js
3 5[Enter]
2.5 4[Enter]
hoge[Enter][Ctrl+C]
8
10
hoge
$ node test.js
3 5[Enter]
2.5 4[Enter]
hoge[Enter][Ctrl+D]
8
10
hoge
$ node test.js <in.txt >out.txt
----------
//out.txt
8
10
hoge
続きを読む

Codeforces 1373 D. Maximum Sum on Even Positions

こんにちは。こどふぉでした。

レートちょっと下がったと思う。悲しい。ちなみに↑の提出コードはSubmission #85050166 - Codeforces

さて、今回は、本番中に解けなかったDを解きます。

Dについてコンテストページのチャットなどを見ていたら「Kadane's algorithm」というワードが出てきました。それを手掛かりになんとか解けそうな気がしたので解いていきます。

問題

(本家→)Problem - D - Codeforces

(概略)数列 $a$ が与えられる。数列の中から連続する部分列を最大1つ選んで反転させることができる。0-based(0始まり)で数えて偶数番目の数字の和($a_0 + a_2 + \cdots$)を最大にせよ。

$a = \{1,2,1,2,1\}$ のとき、範囲 $[0,4]$ を反転させて $a = \{2,1,2,1,1\}$ にすれば $a_0+a_2+a_4 = 2+2+1 = 5$ で最大。

解法

発想

まず、反転させる範囲は偶数個でないといけない。奇数個の範囲を反転させてもそれぞれの数字のindexの偶奇は変わらないため。

偶数個の範囲を反転させるという操作は、偶数index($:=$indexが偶数の項)の和を考えると「隣同士反転させる」の連続と同じ

$$ a = \{1,2,3,4,5,6\} $$

の全体を反転させると

$$ a = \{6,5,4,3,2,1\} $$

だけど、偶数indexの和について知りたいだけなら

$$ a = \{[2,1],[4,3],[6,5]\} $$

のようにしても同じ。

上記の操作は、各偶数indexが、自分の右側の数を「もらう」ことで偶数indexの和が大きくなっている。一方、以下のような数列 $a$ を考えると

\begin{align} &a = \{9,6,5,4,3,2,1\} → \sum\rm{偶数index} = 9+5+3+1 = 18 \cr &\rm{偶数index}が右からもらうと\cr &a = \{[6,9],[4,5],[2,3],1\} → \sum\rm{偶数index} = 6+4+2+1 = 13 \cdots\rm{減ってる}\cr &\rm{偶数index}が左からもらうと\cr &a = \{9,[5,6],[3,4],[1,2]\} → \sum\rm{偶数index} = 9+6+4+2 = 21 \cdots\rm{増えた!} \end{align}

ということで「左からもらう」ことも考えなければいけない。

以下、$\rm{偶数indexの和} = \sum\rm{偶数index} = a_0+a_2+\cdots$ を $x$ とする。

一旦「右からもらう」ことだけを考える。数列 $a =\{7,8,4,5,7,6\}, x=18$ についてもらったときの利益brを偶数indexの下に書いてみると以下のようになる

i 0 1 2 3 4 5
a[i] 7 8 4 5 7 6
br 1 1 -1

brは場合によっては負にもなるわけだが、ともかく、brの部分列の中で一番大きい括りで「反転」=「右からもらうの連続」をやれば良さそうなので、$a_0, a_2$ が「右からもらう」と $a = \{[8,7],[5,4],7,6\}, x=20$ となる。

一方、「左からもらう」ことも考えてみると、左からもらったときの利益bl

i 0 1 2 3 4 5
a[i] 7 8 4 5 7 6
br 1 1 -1
bl 4 -2

となる。blの部分列のなかで一番大きくなる括りで「反転」すると $a_2$ が「左からもらう」ので $a = \{7,[4,8],5,7,6\}, x=22$ となる。

ということで最適なのは範囲 $1,2$ の反転でした。

Kadane's algorithm

さて、ここまでくると知りたいのが「最大部分配列」の求め方です。

を参考にしました。

最大部分配列問題(maximum subarray problem)とは、与えられた配列に対して、その(連続した)部分配列のうち和が最大となるものの値を求める問題のことです。

Kadane’s Algorithm | 最大部分配列 問題 - Ark's Blog

上記サイトをじっくり読んだら理屈はわかったので詳細は割愛しますが、プログラムとしては

long solve(int n, long[] as) {
    long res = -INF;
    long s = 0;
    foreach(j; 0..n) {
        s = max(s + as[j], as[j]);
        res = max(res, s);
    }
    return res;
}

Kadane’s Algorithm | 最大部分配列 問題 - Ark's Blogより

となるそうです。↑はD言語なのでC++に書き換えると(数値は一旦intで)

int solve(int n, vector<int> &as){
  int res = -INF;
  int s = 0;
  for(int j=0; j<n; j++){
    s = max(s + as[j], as[j]);
    res = max(res, s);
  }
  return res;
}

全然変わってないけど。

brblは利益の配列なので、最大部分配列がそのまま最大の利益となる。よって最初に反転なしの偶数indexの和を出しておいて、「brについての最大部分配列」と「blについての最大部分配列」の(0を超えていて)大きい方を足せば求めたい $x$ になる。

実装

今回は $a$ の条件が $1 \le a_i \le 10^{9}$ なのでres = -1e9と初期値を設定しました。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using vl = vector<ll>;
#define rep(i, n) for(int i=0; i<(int)n; i++)

ll kadane(ll n, vl &a){
  ll res = -1e9;
  ll s = 0;
  for(ll j=0; j<n; j++){
    s = max(s+a[j], a[j]);
    res = max(res, s);
  }
  return res;
}

void solve(){
  int N;cin>>N;
  vl a(N);
  ll sum = 0;
  rep(i,N){
    cin>>a[i];
    if(i%2==0)sum += a[i];
  }
  
  vl br, bl;
  for(int i=0; i<N; i+=2){
    if(i!=0)bl.push_back(a[i-1]-a[i]);
    if(i!=N-1)br.push_back(a[i+1]-a[i]);
  }

  ll add = max({
    0LL,
    kadane(br.size(), br),
    kadane(bl.size(), bl)
    });
  cout<<sum+add<<endl;
}

int main(){
  int t = 1;
  cin>>t;
  while(t--)solve();
}

kadaneで求めた最大部分配列が両方マイナスになる可能性(反転させないのが最適)があるのでll add = max()に初期化子リストで(0LL含め)3項入れています。

ACになりました→Submission #85071102 - Codeforces

まとめ

また一つ賢くなった。えらい。

kadene()の引数の配列の方、参照にする必要ないじゃんって思って直接渡したらメモリ使用量増えてあっそうかぁ……てなった(それはそう)。

参考

Codeforces 1370 D. Odd-Even Subsequence

こどふぉでした。

3完でした。

(二分探索といえばlower_boundや!って思ってたので)↓

二分探索とは

なるほど〜〜〜

探索条件について ok な範囲と ng の範囲を左右から伸ばしていくという感じですね

f:id:o-treetree:20200621200032p:plain

AtCoder灰・茶・緑色の方必見!二分探索を絶対にバグらせないで書く方法│FORCIA CUBE│フォルシア株式会社よりお借りしました

今回の「条件」

配列を端から見て、$s$ を作ってみる。

奇数番目(0-index)を $c$ 以下で構成できるかどうか 偶数番目(0-index)を $c$ 以下で構成できるかどうか

奇数番目を $c$ 以下に抑えられるか判定するために

bool check(int c, int flag){
  int r=0; //作れる配列のサイズ
  for(int i=0; i<n; i++){
    if(r%2==flag || a[i]<=c){
      r++;
    }
  }
  return r>=k;
}

flag=0とする

  • r=偶数のときa[i]に関わらずrを増やす
  • a[i]が $c$ 以下のときrを増やす
  • それ以外はスルー

rをk以上にできるかどうかが判定条件

二分探索

めぐる式二分探索とかいうやつ

  int ok = 1e9;
  int ng = 0;

  while(abs(ok-ng)>1){
    int mid = (ok+ng)/2;
    if(check(mid,0) || check(mid,1)){
      ok = mid;
    }else{
      ng = mid;
    }
  }
  cout<<ok<<endl;

初期値について、$1\le a_i \le 10^{9}$ より

  • $s$ の偶数/奇数番目を $10^{9}$ 以下に抑えるのは常に可能
  • $s$ の偶数/奇数番目を $0$ 以下に抑えるのは常に不可能

というお気持ち。

完成

#include <bits/stdc++.h>
using namespace std;

int n,k;
vector<int> a;
/* flag 通過 */
bool check(int c, int flag){
  int r=0; //作れる配列のサイズ
  for(int i=0; i<n; i++){
    if(r%2==flag || a[i]<=c){
      r++;
    }
  }
  return r>=k;
}

void solve(){
  cin>>n>>k;
  a.resize(n);
  rep(i,n)cin>>a[i];

  int ok = 1e9;
  int ng = 0;

  while(abs(ok-ng)>1){
    int mid = (ok+ng)/2;
    if(check(mid,0) || check(mid,1)){
      ok = mid;
    }else{
      ng = mid;
    }
  }
  cout<<ok<<endl;
}

int main(){
  int t = 1;
  //cin>>t;
  while(t--)solve();
}

おしまい

二分探索がなんとなくわかったと思う。

AtCoder ABC171 反省会 [A,B,C,D,E完]

こんにちは。ABC171でした。

5完でした!!!

即席解説記事行きます

A

愚直な実装。

#include <bits/stdc++.h>
using namespace std;

int main(){
  char x;cin>>x;
  if('A'<=x && x<='Z')cout<<"A\n";
  else cout<<"a\n";
}

B

ソートして小さい方から $K$ 個取る。

#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for(int i=0; i<(int)n; i++)
#define all(v) (v).begin(), (v).end()

int main(){
  int N,K;cin>>N>>K;
  vector<int> p(N);
  rep(i,N)cin>>p[i];

  sort(all(p));

  int ans = 0;
  rep(i,K){
    ans += p[i];
  }
  cout<<ans<<endl;
}

C

「26進法」のように実装。下の桁から順にvectorに突っ込んでいって、最後逆転させて出す。 (追記:最初27進法って書いてましたが26進法ですね)

続きを読む

Codeforces #650 D. Task On The Board を解く(こどふぉ 問題文和訳あり)[C++]

こんにちは。まさかのこどふぉへ進出。

Cまで解きました。

で、Dの解説とサンプルコード(Codeforces Round #650 (Div. 3) Editorial - Codeforces)を読んだらなるほどぉ〜〜〜ってなったので自分で書いてみます。

問題

https://codeforces.com/contest/1367/problem/D

できる限り和訳してみます。

Polycarp(誰?)は英小文字(a-z)からなる文字列 $S$ を書きました。

その後、彼は $S$ からいくつか文字を消し、残っている文字をランダムに並び替えて文字列 $T$ を書きました。あなたは、与えられた情報から $T$ をあててください。

  • $T$ の長さは $ m $ であり、それぞれの文字に次のルールから得られる数字がついています。数字は数列 $ \{ b_1, b_2, \cdots , b_m \} $ として与えられます。
  • $b_i$ は、$T$ の $i$ 番目の文字 $T_i$ について、$T$ のなかで $T_i$ よりアルファベット順が大きい文字 $T_j$ との距離 $|i-j|$ の総和です。

例えば、$T = \text{abzb}$ のとき

  • $T_1$ はaなので $T$ の中でアルファベット順でaより後ろである文字との距離の総和をとって $b_1 = |1−2|+|1−3|+|1−4|=1+2+3=6$
  • $T_2$ はbなので $T_3 = \rm{z}$ がアルファベット順で後ろにあるため、$b_2 = |2−3|=1$
  • $T_3$ はzなので、$T$ の中にアルファベット順がzより後ろである文字はないため、$b_3 = 0$
  • $T_4$ はbなので $T_3 = \rm{z}$ がアルファベット順で後ろにあるため、$b_4 = |4−3|=1$

従って、$T = \text{abzb}$ のとき $b = \{6,1,0,1\}$ が与えられます。

与えられた文字列 $S$ と数列 $b$ から、$T$ としてあり得るものを見つけてください。

$T$は次の2つの要求を同時に満たす必要があります。

  • $T$ は、$S$ からいくつかの(0文字も可)文字を消して任意の順で並び替えて得られる
  • $T$ から上記のルールに従って求めた数列は、入力で与えられる $b$ と一致する。

入力

第1行目に整数 $q$ : テストケースの数 $(1≤𝑞≤100)$

続いて $q$ 個のテストケースが与えられます。それぞれのテストケースについて

  • 1行目 : $S (1 \le |S| \le 50)$ $S$ は英小文字からなる
  • 2行目 : 整数 $ m (1≤m≤|S|) $ : 数列 $b$ のサイズ
  • 3行目 : 整数 $b_1, b_2, \cdots , b_m (0 \le b_i \le 1225)$

それぞれのテストケースについて答えが存在することが保証されます。

出力

$q$ 行出力します。$k$ 行目には $k$ 番目のテストケースに対する回答 $T$ を出力します。複数の答えが存在するとき、そのうちの1つを出力します。

入出力例

  • 入力
4
abac
3
2 1 0
abc
1
0
abba
3
1 0 1
ecoosdcefr
10
38 13 24 14 11 5 3 24 17 0
  • 出力
aac
b
aba
codeforces
  • 1つ目のテストケースの回答としてaac aabがあり得ます。
  • 2つ目のテストケースの回答としてa b cがあり得ます。
  • 3つ目のテストケースの回答としてはabaのみですが、ここでのbは $S$ の2文字目に由来するか3文字目に由来するかを区別しません(原文: In the third test case, only the string 𝑡 equals to "aba" is suitable, but the character 'b' can be from the second or third position.)。

解法

  • $b_i = 0$ であるところには、$T$ の中で最もアルファベット順が後ろのものが入る(その文字が $S$ の中に十分存在すれば)。
  • そこを埋めたら、他の場所の $b_j$ から $|i-j|$ を引く。

よくわからんので例を。入力例4からちょっと改変

$T = \text{ecoosdcefrep} , b = \{38, 13, 24, 14, 11, 5, 3, 24, 17, 0\}$ のとき(epが増えてる)、$T$ をアルファベット順に並び替えておくとccdeeefooprs


38 13 24 14 11 5 3 24 17 0
s

$b_{10} = 0$ なので $T_{10} = \rm{s}$ とする。その他の $b_j$ には、そこに入る文字がなんであろうとすべて $|10-j|$ が加算されているのでそれぞれ差っ引いてあげる。そうすると $i=10$ を無視した問題に帰結する。


38 13 24 14 11 5 3 24 17 0
-9 -8 -7 -6 -5 -4 -3 -2 -1 s
29 5 17 8 6 1 0 22 16

$T$ に残っている文字はccdeeefoopr。$b_{9} = 0$ なので $T_{9} = \rm{r}$ とする。


38 13 24 14 11 5 3 24 17 0
-9 -8 -7 -6 -5 -4 -3 -2 -1 s
29 5 17 8 6 1 0 22 16
-6 -5 -4 -3 -2 -1 r -1 -2
23 0 13 5 4 0 21 14

$T$ に残っている文字はccdeeefoop。$b_i = 0$ となる場所が2つあるがどちらかだけにpを入れるなどしてはいけない。pは捨てて、oが2つあるのでそれを入れる。


引き算はそれぞれのoからの距離を重ねて引く。

38 13 24 14 11 5 3 24 17 0
-9 -8 -7 -6 -5 -4 -3 -2 -1 s
29 5 17 8 6 1 0 22 16
-6 -5 -4 -3 -2 -1 r -1 -2
23 0 13 5 4 0 21 14
-1 o -1 -2 -3 -6 -7
-5 -3 -2 -1 o -2 -3
17 9 1 0 13 4

$T$ に残っている文字はccdeeef。$b_{5} = 0$ なので $T_{5} = \rm{f}$ とする。


38 13 24 14 11 5 3 24 17 0
-9 -8 -7 -6 -5 -4 -3 -2 -1 s
29 5 17 8 6 1 0 22 16
-6 -5 -4 -3 -2 -1 r -1 -2
23 0 13 5 4 0 21 14
-1 o -1 -2 -3 -6 -7
-5 -3 -2 -1 o -2 -3
17 9 1 0 13 4
-4 -2 -1 f -3 -4
13 7 0 10 0

$T$ に残っている文字はccdeee。$b_4 = b_9 = 0$ なので $T_4 = T_9 = \rm{e}$ とする。余ったeは捨てる。


同様に残りを埋める

38 13 24 14 11 5 3 24 17 0
-9 -8 -7 -6 -5 -4 -3 -2 -1 s
29 5 17 8 6 1 0 22 16
-6 -5 -4 -3 -2 -1 r -1 -2
23 0 13 5 4 0 21 14
-1 o -1 -2 -3 -6 -7
-5 -3 -2 -1 o -2 -3
17 9 1 0 13 4
-4 -2 -1 f -3 -4
13 7 0 10 0
-3 -1 e -4
-8 -6 -1 e
2 0 5
-2 d -5
0 0
c c
c o d e f o r c e s

実装

$S$ に含まれるアルファベットを文字と個数のmapで管理。

cordforces #650 D

Acceptedなりました。

おしまい

はじめてのこどふぉ。

3完2WAで計91ペナでした。

AtCoder ABC170 E - Smart Infants を解く [C++]

こんにちは。昨日はABC170でした。

o-treetree.hatenablog.com

Eが、multisetとかいうのを使って実装すれば解けるよって解説に書いてあったのでやってみたところ、1つだけWAが出てわけわからんになったので(https://atcoder.jp/contests/abc170/submissions/14384348)ここで整理しながら丁寧に解いてみよう、という記事です。

(!!!まだACできてません何がおかしいんだ!?!?!?)

↑ってなってましたが解決しました!

  • 問題
  • 用意するもの
    • 使い方確認
  • 各クエリに対する処理
    • まずは、転園に関わる園の最強園児レートをmaxenjiから削除する
    • 転園処理
    • maxenjiにFとTの新しい最強園児レートを入れる
    • 転園した幼児の所属園番号を書き換えてあげる
    • 「平等さ」の出力
  • 入力
  • 提出(WA)
  • 解決
    • 解決策1
    • 解決策2
  • まとめ
  • 参考

問題

用意するもの

公式解説pdf(https://img.atcoder.jp/abc170/editorial.pdf)を参考に。以下のものを用意します。今回は、園児の番号を1-indexで(入力そのままに)扱うのでサイズN+1の配列がでてくる。

  • 各園児のレートを表す配列vector<int> rate(N+1);。これは最初に入力したら以後書き換えはしない
  • 各園児が所属する幼稚園の番号を示すvector<int> belong(N+1);。これは適宜書き換えていく。
  • 各幼稚園に幼稚園に所属する幼児のレートの順序付き多重集合(の配列)vector<multiset<int>> youchien(200010);。配列のサイズとしては $2\times 10^{5}$ よりちょっと多めにしておく(一応)。
  • 各幼稚園の最強園児のレートの順序付き多重集合 multiset<int> maxenji;
続きを読む
プライバシーポリシー ・お問い合わせはこちら