予鈴

競プロとか備忘録とか…

D - Fennec VS. Snuke

問題文

atcoder.jp

解き直すと別の解法で解けたので書きます。

解法

ゲームには貪欲法が存在し、v_{1}からv_{N}に向かう頂点を埋めていくのが最適です。
与えられるグラフは木なので、任意の頂点 u,vに関して、 path_{u,v}がただ一つ存在します。
したがって、pathに含まれる頂点のうちどれがv_{1}に含まれるか、v_{N}に含まれるかを計算します。
後はv_{1},v_{N}にくっついている頂点を数えて数を比較すればどちらが勝つかわかります。

#include<bits/stdc++.h>
using ll  = long long;
#define int ll
#define rep(i,n) for(int i=0;i<n;i++)
#define all(in) in.begin(), in.end()
using namespace std;
signed main(){
    int n; cin >> n;
    vector<vector<int>>edge(n);
    rep(i,n-1){
        int s,t; cin >> s >> t;
        edge[--s].push_back(--t);
        edge[t].push_back(s);
    }
    vector<bool>visit(n,false);
    vector<bool>ispath(n,false);
    function<bool(int)> dfs = [&](int v){
        visit[v] = true;
        bool flag = (v == n-1);
        for(auto nv : edge[v]){
            if(visit[nv])continue;
            if(dfs(nv))flag = true;
        }
        return ispath[v] = flag;
    };
    dfs(0);
    // all vertex was checked.
    int margin = accumulate(all(ispath),0LL) - 2;
    margin = margin / 2 + margin % 2;
    vector<int>grid(n,-1);
    function<int(int,int)>cntup = [&](int v, int atrb){
        int res = 1;
        grid[v] = atrb;
        for(auto nv : edge[v]){
            if(grid[nv] != -1)continue;
            if(ispath[nv] == false or atrb == 1) res += cntup(nv,atrb);
            else if(ispath[nv] and margin > 0 )--margin,res += cntup(nv,atrb);
        }
        return res;
    };
    int f = cntup(0,0), s = cntup(n-1,1);
    assert(f + s == n);
    cout << (f > s ? "Fennec" : "Snuke") << endl;
}


Codeforces Round #544 (Div. 3) D. Zero Quantity Maximization

問題

解き直すと一瞬で通って悔しすぎるので、ここで供養しようと思います。
codeforces.com

解法

問題文に与えられた式を整理すると、
 d = \frac{-b[i]}{a[i]}

任意の正の有理数は、ただ一つの既約分数を用いて表せるという性質を用いる。
mapのkeyを
 b[i] / gcd(b[i],a[i]) + yebityon + a[i] / gcd(b[i],a[i])
のような文字列としてカウントアップしていけば良い。
ただし、
 b[i]  <0 \land  a[i] < 0 の時はdは負になり、
 b[i]  >0 \land  a[i] < 0 と  b[i]  <0 \land  a[i] >0は値的には等しくなるので、注意。
 b[i]  == 0 \land  a[i]  == 0の時は任意のdに対して0になるので、別途数えてあとで足すと良い。

#include<bits/stdc++.h>
using ll  = long long;
#define int ll
#define rep(i,n) for(int i=0;i<n;i++)
#define all(in) in.begin(), in.end()
#define MP make_pair
#define INF (sizeof(int) == 4 ? (int)1e9:(int)1e18)
#define EPS 0.0000000001
using namespace std;
template<class T, class S> void cmax(T &a, const S &b) { if (a < b)a = b; }
using ld  = long double;
template<class T> T gcd(T a,  T b) {return b ? gcd(b, a % b) : a;}
template<class U> U lcm( U m, U n ){if((0LL == m )||( 0LL == n ))return 0; return ((m / gcd(m, n)) * n); }
signed main(){
    int n;
    cin >> n;
    vector<int>a(n);
    vector<int>b(n);
    for(auto& e : a) cin >> e;
    for(auto& e : b) cin >> e;
    map<string,int>mp;
    int res = 0;
    for(int i = 0; i < a.size(); i++){
        if(a[i] == 0 and b[i] == 0){
            res++;
            continue;
        }
        if(a[i] == 0)continue;
        int div = gcd(abs(a[i]),abs(b[i]));
        if((long double)b[i] * -1 / a[i]  >= 0){
            // d >= 0
            mp[to_string(abs(b[i]) / div) + "yebityon!" + to_string(abs(a[i])/div)]++;
        }else{
            // d < 0
            //分子を 負 にすることに統一
            mp[to_string(-1 * abs(b[i]) / div) + "yebityon!" + to_string(abs(a[i])/div)]++;
        }
    }
    int ans = 0;
    for(auto itr : mp){
        cmax(ans, itr.second);
    }
    cout << ans + res << endl;
    
}

A. Technogoblet of Fire の日本語訳

問題文が理解不能すぎたので訳しました。間違ってたらコメントください。
というか訳しても意味がよくわからないけど
codeforces.com

m coderトーナメントがもうすぐ開催されます。 m校の学校が参加し、各校につき1人の選手だけが参加することができます。

すべての参加者の合計はn人です。トーナメントの前に、すべての学生は名前と自分の学校をTechnogoblet of Fireに入れました。
Technogobletは各学校で最も強い学生を選びます。

Arkadyは特定のk人の生徒がTechnogobletによって選ばれることを望んでいるハッカーです。
残念ながら、全員が自分の学校で最強であるわけではありません。
しかし、Arkadyは、新しい学校名を作り上げて、生徒の学校名を、作り上げた学校名に置き換えることができます。
あなたはそれぞれの作り上げた学校名を繰り返し使用することはできません。
Technogobletは作り上げた学校の名学校で最も強い生徒を選びます。

あなたは各生徒の力と所属している学校を知っています。
K人の生徒が選ばれるために必要な最小の学校数を計算してください。

設定が謎すぎるし、Technogobletがなにかわからないし、何より k Chosen Ones*1 がわからなさすぎる。

*1:特定の人って意味らしい。変にcapitalにするのやめて

E-Union

Problem

atcoder.jp

Solution

・愚直解は、 iに対し、各0...a_i個使ってシュミレーションが考えられるが、計算量的に間に合わない。
・配列dpを次のように定義する
dp[i][j] :=   数字 i  j 個使ったときの場合の数
・更新は
dp[i][x + y] = dp[i][x] + dp[i-1][2*y]

・更新した値をもう一度更新しないように、後ろから更新していかないと行けない。
・特に、 y == 0の場合は更新しないこと。
i番目の数字をいくつ使用しても、i-1番目までの計算結果は繰り返し利用できるので、そのあたりに着目するとDPに到達できるかも…?

#include<bits/stdc++.h>
using ll  = long long;
#define int ll
#define rep(i,n) for(int i=0;i<n;i++)
using namespace std;
auto dp = vector<vector<int>>(500,vector<int>(3000,0LL));
signed main(){
    int t;
    cin >> t;
    vector<int>v(t);
    for(auto& e : v) cin >> e;
    rep(i,v.size()){ fill(dp[i].begin(),dp[i].begin() + v[i] + 1,1LL);}
    int mod = 1000000007;
    rep(i,dp.size() - 1){
        dp[i][0] = 1;
        if(i == 0)continue;
        for(int j = dp[i].size(); j >= 0;--j){
            if(dp[i][j] == 0 )continue;
            for(int k = dp[i-1].size() - 1; k >= 1; --k){
                if( k % 2 or j + k/2 >= dp[i].size())continue;
                if(dp[i-1][k] == 0)continue;
                dp[i][k/2 + j] += dp[i][j] * dp[i-1][k];
                dp[i][k/2 + j] %= mod;
            }
        }
    }
    int ans = 0;
    for(int i = 0;  i < dp.size(); ++i){
        ans += dp[i][1];
        ans %= mod;
    }
    cout << ans << endl;
}

NStextFieldをNSMenuに埋め込みたい。

この記事は備忘録です。

NStextFieldをNSmenuに埋め込んで、上のstatus barから入力受け取れたらいいんだけどな〜って思ってたんですが、どうやらバグ*1でできないらしいです。

具体的には、一度focusが離れると入力を受付なくなるらしい…

 

なにか良い解決策があれはご教示ください。

 

類似の質問がstack overflowにいくつかありました。

stackoverflow.com

stackoverflow.com

stackoverflow.com

*1:2019/2月現在

数式(?)を逆ポーランド記法に変換して計算する。

面白そうだな〜と思っていたので、実装しました。
要するに電卓です。
10+4*(10/2+7)/2+2+(3+(2+2)) ←こういうのを計算してくれます。負数と小数点には対応してません。
楽しかったので自己満足記事です。verifyはしてないので、使用は自己責任でお願いします。
バグがあれば教えてください。


#include <iostream>
#include <string>
#include <vector>
#include <stack>

using namespace std;

bool isOp(string c){return c == "+" or c == "*" or c == "-" or c == "/" or c == ")" or c == "("; }
bool isOp(char c){return c == '+' or c == '*' or c == '-' or c == '/';}
bool isNum(char c){return '0' <= c and c <= '9';}
bool isNum(string s){return !(isOp(s));}

auto  parser(string s){
    string num = "";
    vector<string>token;
    for(auto c : s){
        if(isNum(c)){
            num.push_back(c);
        } else {
            if(not num.empty()){
                token.push_back(num);
                num.clear();
            }
            token.push_back(string() + c);
        }
    }
    if(not num.empty()) token.push_back(num);
    return token;
}
auto OperandPriority(string lower,string higher){
    return ((lower == "+" or lower == "-") and (higher == "*" or higher == "/")) ;
}
auto constructRPN(vector<string>token){
    //逆ポーランド記法の文字列配列を生成。
    stack<string>st;
    vector<string> buffer;
    for(auto str : token){
        if(isNum(str)){
            buffer.push_back(str);
        } else {
            if(str  == ")"){
                while(st.top() != "("){
                    buffer.push_back (st.top());
                    st.pop();
                }
                assert(st.top() == "(");
                st.pop();
            } else if(str == "(" or st.empty()){
                st.push(str);
            } else if(OperandPriority(st.top(),str)){
                st.push(str);
            } else {
                while(not st.empty() and st.top() != "(" and !OperandPriority(st.top(),str)){
                    buffer.push_back(st.top());
                    st.pop();
                }
                st.push(str);
            }
        }
    }
    while(not st.empty())buffer.push_back(st.top()),st.pop();
    return buffer;
}
int execute(vector<string> l){
    stack<int>num;
    for(auto s: l){
        if(isNum(s)){
            num.push(stoi(s));
        } else {
            int res = 0;
            int val1 = num.top();num.pop();
            int val2 = num.top();num.pop();
            char c = s.front();
            switch (c) {
                case '+':
                    res =  val1 + val2;
                    break;
                case '*':
                    res =  val2 * val1;
                    break;
                case '-':
                    res =  val2 - val1;
                    break;
                default:
                    res =  val2 / val1;
                    break;
            }
            num.push(res);
        }
    }
    return num.top();
}
int main() {
    string s; cin >> s;
    auto token = parser(s);
    auto Literal =  constructRPN(token);
    for(auto s: Literal)cout << s; cout << endl;
    cout << execute(Literal) << endl;
}

D - Number of Amidakuji

問題

atcoder.jp

考えていたこと

dpまではすぐにたどりつけたが、「正しいあみだくじ」の本数を求める方法がわからず。
(i,j)から(i,j+1),(i+1,j+1),(i-1,j+1)に推移できる。

解法

(i,j)番目にいるときの、あり得るあみだくじの場合の数を計算する。
・当たり前だけど、あみだくじの状態が決まれば推移先は一つに決まる。(条件が厳しくなっている。)
・なんちゃってdynamic_bitsetを作りました。

#include<bits/stdc++.h>
using ll  = long long;
#define int ll
#define rep(i,n) for(int i=0;i<n;i++)
using namespace std;
template<typename Head, typename Value> auto vectors(const Head &head, const Value &v) { return vector<Value>(head, v); }
template<typename Head, typename... Tail> auto vectors(Head x, Tail... tail) { auto inner = vectors(tail...); return vector<decltype(inner)>(x, inner); }
int MOD = 1000000007;
class dynamic_bitset {
private:
    long long bit;
    string sbit                = "";
    unsigned long long bitsize = 0;
    
public:
    dynamic_bitset(int _bit) : bit{_bit} {
        for (int t = 1, mask = 2; bitsize == 0; t++, mask *= 2) {
            if (mask - 1 >= bit) bitsize = t;
        }
        for (int i = 0; i < bitsize; i++) {
            sbit = ((bit >> i & 1) == 1 ? "1" : "0") + sbit;
        }
    }
    dynamic_bitset(int _bit, int bitrange) : bit{_bit}, sbit{} {
        for (int t = 1, mask = 2; bitsize == 0; t++, mask *= 2) {
            if (mask - 1 >= bit) bitsize = t;
        }
        for (int i = 0; i < bitrange; i++) {
            sbit = (i < bitsize and ((bit >> i & 1) == 1) ? "1" : "0") + sbit;
        }
    }
    auto test(int _idx) {
        assert(_idx < sbit.size() && "index  out of range");
        return sbit[_idx] == '1';
    }
    void set(int _idx) {
        assert(_idx < sbit.size() && "findex out of range");
        sbit[_idx] = '1';
    }
    auto size() { return sbit.size(); }
    bool isAll() {
        return accumulate(sbit.begin(), sbit.end(), true,
                          [](bool r, char c) { return r && c == '1'; });
    }
    bool none() {
        return accumulate(sbit.begin(), sbit.end(), true,
                          [](bool r, char c) { return r && c == '0'; });
    }
    auto to_string() { return sbit; }
};
signed main(){
    int h,w,k;
    cin >> h >> w >> k;
    auto dp = vectors(300, 300,0LL);
    dp[1][1] = 1;
    for(int j = 1; j <= h; j++){
        for(int i = 1; i <= w; i++){
            for(int bit = 0; bit < (1 << (w - 1));bit++){
                auto db = dynamic_bitset(bit,w-1);
                bool isValid = true;
                rep(i,(int)db.size() - 1){
                    if(db.test(i) and db.test(i+1))
                        isValid = false;
                }
                if(!isValid)continue;
                if(i+1 <= w and db.test(i-1)){
                    dp[i+1][j+1] = (dp[i+1][j+1] + dp[i][j]) % MOD;
                } else if(i-1 >= 1 and db.test(i-2)){
                    dp[i-1][j+1] = (dp[i-1][j+1] + dp[i][j]) % MOD;
                } else {
                    dp[i][j+1]   = (dp[i][j+1]   + dp[i][j]) % MOD;
                }
            }
        }
    }
    cout << dp[k][h+1] << endl;
}