競技プログラミング練習会

2020 Normal

第11回

復習とか

担当:zeke

告知

月末にLT大会開催!!

  • LTとはLightning Talks (ライトニングトーク)の略

  • 5分くらいでいろいろしゃべります

  • 詳細は内部wiki

何をしゃべればいいのか

みんな参加しよう!!

自己紹介

自己紹介

  • KMCid:zeke(本名:岡島和希)
  • 工学部工業化学科2回
  • AtCoder水(1505)
  • 自堕落生活を送る
  • そのせいで先週のABCを逃す
  • 月1の授業に2回しか出ていないのは、さすがにやばいですかね?
  • 強化学習とか勉強中
  • #zeke-memo

とりあえずコンテスト

  • A~C:先週のABCのC~E
  • D:XORの基本
  • E:ゲーム問題

先週と同じく

19:00ぐらいから

解説やります

問題概要

  • 配列に含まれない整数からXの絶対値差が一番小さいやつを見つけて!
  • 制約(1X100,1≤X≤1000N1000≤N≤100.1pi100)

考えたこと

  • 制約が小さいので絶対値差が少ないやつから配列内にあるかどうかを調べればよさそう
  • 愚直に毎回配列をなめていくとO(N^2)かかるが、二分探索(C++だとbinary_search)を使うとO(NlogN)

コード例

int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(10);
    ll x,n;
    cin>>x>>n;
    V vec(n);
    rep(i,n)cin>>vec[i];
    sort(all(vec));
    ll res=0;
    rep(i,200){
        if(!binary_search(all(vec),x-i)){
            res=x-i;
            break;
        }
        if(!binary_search(all(vec),x+i)){
            res=x+i;
            break;
        }
    }
    cout<<res<<endl;
}

問題概要

  • 配列の中で自身以外で自身を割り切ることのできない数がいくつあるか?
  • 制約:1≤N2×10^5,1≤N≤2×10^5,1Ai10^6

考えたこと

  • まずは愚直に考えてみる
  • 愚直に一個一個確かめていったらO(N^2)
  • O(N^2)だとTLEになるいつものやつ

考えたこと

  • こういうやつは素因数分解するとうまくいくことが多い(??)(経験からです)
  • しかし、うまくいかない

考えたこと

  • 今度は逆に考えてみる(よくある)
  • 制約も整数の上限が1e6になってる(1e9のことが多いのに…)
  • 割られる数があるかどうかを調べるのではなく、自分が割れる数が配列の中にあるかどうかを調べる
  • この考え方はエラトステネスの篩 とよく似ている
  • ↑は素数判定法に使ったやつ

考えたこと

  • まず配列をsortしておこう(このテクはよく使う、配列順序に意味がないやつはとりあえずsortしておこう)
  • 小さい整数から見ていく
  • 見ている整数を二倍、三倍したものにcheckをつける
  • ↑を1e6に達するまでやる
  • 次に見る整数から、すでに同じ整数を見ていたら飛ばすということをやる(ここ重要)

考えたこと

  • ここで大事になってくるのが計算量
  • 計算量が最大になるのは配列の中身が全部違うとき

2e5

1 2 3 4 …

  • 計算量は(1e6/1+1e6/2+…)
  • こういった級数を調和級数といい、O(NlogN)になることが知られている
  • ゆえに計算量は最初にソートする分と調和級数の分を足して、O(NlogN+A_max log A_max)となる

コード例

int main() {
    ll n;
    cin>>n;
    vector<bool> dp(1e6+100,false);
    V vec(n);
    map<ll,ll> mp;
    map<ll,ll> mp2;
    rep(i,n){
        cin>>vec[i];
        mp[vec[i]]++;
    }
    sort(all(vec));
    rep(i,n){
        ll index=2;
        if(mp2[vec[i]]==1){
            continue;
        }
        mp2[vec[i]]++;
        while(vec[i]*index<=1e6+10){
            dp[vec[i]*index]=true;
            index++;
        }
    }
    ll res=0;
    rep(i,n){
        if(dp[vec[i]]||mp[vec[i]]>=2){
        //    cout<<vec[i]<<endl;
            res++;
        }
    }
    cout<<n-res<<endl;
}

C:Small Infants

ごめん、まだ解いてない

データ構造で殴るらしい

問題概要

  • 配列から二つ選んだ整数の排他的論理和のすべての和を求めてね
  • 制約:2N3×10^5

まず排他的論理和とは?

  • wiki
  • 競プロでよく出てくる論理演算
1 1 0
0 1 1
1 0 1
0 0 0

ANDとかORとかとはまた違う

競プロでのXORのよくある考え方

  • 二進数の桁ごとに考えることが多い
  • ↑とりあえずXORが出てきたら二進数に直すのが鉄則
  • X^(X+1)=1
  • X^X=0
  • X=X^X^X=X^X^X^X^X…

この問題の考え方

入力例1を考える

まずは愚直にやるとO(N^2)当然間に合わない

10進数 2^1 2^0
1 0 1
2 1 0
3 1 1

E:ABS

ゲーム問題

DPを考えてみる

Text

Made with Slides.com