くれなゐの雑記

例を上げて 自分で手を動かして学習できる入門記事を多めに書いています

Codeforces #367 Div.2 C. Hard problem

problem

codeforces.com

問題要約

コスト C_0, C_1, \cdots , C_{N-1}

文字列 S_0, S_1, \cdots , S_{N-1}
が与えられる。

\mathrm{reverse}(S_i) をするために, コスト C_i がかかる

うまいこと\mathrm{reverse}をして, 文字列を辞書順に昇順(S_0 \leq S_1 \leq \cdots \leq S_{N-1})となるようにしたい.

昇順にできるとき, 最小の総コストを求めて, どう頑張ってもできない時は-1を出力する

解き方

dpを使う \mathcal{O}(N)
dp[n][f] : n番目の文字列までを昇順にするときに必要な最低のコスト
ここで, f=1の時, n番目をひっくり返すときを意味し, f=0の時, n番目をひっくり返さないことを意味する.

更新式は数式で表記すると面倒なので, ソースコード参照

事前処理として, Sを反転させた配列を用意しておく

注意事項

c \in [0,10^9]がやたらでかいので, オーバーフロー, infの定義に注意する

最終的な出力の最大値は, 10^{14}


SourceCode

#include <iostream>
#include <queue>
#include <map>
#include <list>
#include <vector>
#include <string>
#include <limits>
#include <cassert>
#include <fstream>
#include <cstring>
#include <bitset>
#include <iomanip>
#include <algorithm>
#include <functional>
#include <cstdio>
#include <ciso646>

using namespace std;

#define FOR(i,a,b) for (int i=(a);i<(b);i++)
#define RFOR(i,a,b) for (int i=(b)-1;i>=(a);i--)
#define REP(i,n) for (int i=0;i<(n);i++)
#define RREP(i,n) for (int i=(n)-1;i>=0;i--)

#define inf 0x3f3f3f3f
#define linf 0x3f3f3f3f3f3f3f3f
#define PB push_back
#define MP make_pair
#define ALL(a) (a).begin(),(a).end()
#define SET(a,c) memset(a,c,sizeof a)
#define CLR(a) memset(a,0,sizeof a)
#define pii pair<int,int>
#define pcc pair<char,char>
#define pic pair<int,char>
#define pci pair<char,int>
#define VS vector<string>
#define VI vector<int>
#define DEBUG(x) cout<<#x<<": "<<x<<endl
#define MIN(a,b) (a>b?b:a)
#define MAX(a,b) (a>b?a:b)
#define pi 2*acos(0.0)
#define INFILE() freopen("in0.txt","r",stdin)
#define OUTFILE()freopen("out0.txt","w",stdout)
#define in scanf
#define out printf
#define ll long long
#define ull unsigned long long
#define eps 1e-14
#define FST first
#define SEC second

// n次元配列の初期化。第2引数の型のサイズごとに初期化していく。
template<typename A, size_t N, typename T>
void Fill(A (&array)[N], const T &val){
    std::fill( (T*)array, (T*)(array+N), val );
}

int main(void) {
	int N; cin >> N;
	vector<int> C(N); for(auto &a:C) cin >> a;
	vector<string> S(N); for(auto &a:S) cin >> a;
	vector<string> revS = S;
	REP(i,N) reverse(ALL(revS[i]));

	ll dp[100001][2] = {};
	REP(i,100001) REP(j,2) dp[i][j] = (ll)(1e14);
	dp[0][0]  = 0;
	dp[0][1] = C[0];

	FOR(index, 0,N-1){
		if(S[index] <= S[index+1]) dp[index+1][0] = min(dp[index+1][0], dp[index][0]);
		if(revS[index] <= S[index+1]) dp[index+1][0] = min(dp[index+1][0], dp[index][1]);
		if(S[index] <= revS[index+1]) dp[index+1][1] = min(dp[index+1][1], dp[index][0]+C[index+1]);
		if(revS[index] <= revS[index+1]) dp[index+1][1] = min(dp[index+1][1], dp[index][1]+C[index+1]);
	}

	ll res = min(dp[N-1][0], dp[N-1][1]);

	cout << (res>=1e14?-1:res) << endl;

	return 0;
}

Codeforces #366 Div.2 C. Thor

Question

Problem - C - Codeforces

問題要約

q個のクエリが渡される クエリは以下の書式で渡される

t x

ここで,
- t=1の時: アプリケーションxが通知を生成する
- t=2の時: アプリケーションxが生成した通知をすべて読む (過去に読んだやつももう一度読む)
- t=3の時: 生成された通知を, アプリケーション関係なく, 最初から数えてx回目までのものをすべて読む.

各クエリ毎に, まだ読んでいない通知を出力する.

やること

以下の4つを用意する(少し冗長な気がする)
(Nは, アプリケーションの個数)

  • app[アプリの番号]: 何個未読の通知を持っているか
  • queue addedTime[アプリの番号]: いつ通知されたか
  • memo[生成された順番]: なにが生成されたか
  • sum: 現在の個数

この4つを実装し, 問題文のとおりに書くだけ 時間がギリギリなので.at()とかでアクセスしたらTLEした.

SourceCode

#include <iostream>
#include <queue>
#include <map>
#include <list>
#include <vector>
#include <string>
#include <limits>
#include <cassert>
#include <fstream>
#include <cstring>
#include <bitset>
#include <iomanip>
#include <algorithm>
#include <functional>
#include <cstdio>
#include <ciso646>
#include <array>
#include <cmath>

using namespace std;

#define FOR(i,a,b) for (int i=(a);i<(b);i++)
#define RFOR(i,a,b) for (int i=(b)-1;i>=(a);i--)
#define REP(i,n) for (int i=0;i<(n);i++)
#define RREP(i,n) for (int i=(n)-1;i>=0;i--)

#define inf 0x3f3f3f3f
#define uinf 0x3f3f3f3f3f3f3f3f
#define CLEAR(a) a = decltype(a)()
#define MP make_pair
#define ALL(a) (a).begin(),(a).end()
#define pii pair<int ,int>
#define pcc pair<char,char>
#define pic pair<int,char>
#define pci pair<char,int>
#define VS vector<string>
#define VI vector<int>
#define VVI vector<vector<int>>
#define DEBUG(x) cout<<#x<<": "<<x<<endl
#define pi 2*acos(0.0)
#define INFILE() freopen("in.txt","r",stdin)
#define OUTFILE() freopen("out.txt","w",stdout)
#define ll long long
#define ull unsigned long long
#define eps 1e-14
#define FIN std::ifstream cin("D:\input.txt")
const int MOD = 1e9 + 7;

signed int main(void) {
	ios::sync_with_stdio(false);
	int N, Q; cin >> N >> Q;
	vector<int> app(N + 1);
	vector<queue<int>> addedTime(N + 1);
	vector<int> memo; memo.reserve(Q+1);
	int readtime = 0;
	int sum = 0;
	REP(i, Q) {
		int t, x; cin >> t >> x;
		if (t == 1) {
			memo.push_back(x);
			app[x]++;
			addedTime[x].push(memo.size()-1);
			++sum;
		}
		if (t == 2) {
			sum -= app[x];
			app[x] = 0;
			while (not addedTime[x].empty()) {
				int que = addedTime[x].front(); addedTime[x].pop();
				memo[que] = 0;
			}
		}
		if (t == 3) {
			for (; readtime < x; ++readtime) {
				app[memo[readtime]]--;
				if(memo[readtime] != 0) --sum;
			}
		}
		cout << sum << endl;
	}
	return 0;
}

感想

基本的にはやるだけ… のはずだった WAWAWAWAWA....
C++で書いたけど結構時間ギリギリになってしまった
2完で終わる悲しい結末になった

Undertale ubuntuのsaveファイルの場所

Gルート後、Gルート用のsaveファイルになってしまうらしい
とにかく, saveファイルをリセットしたい時がある

ところで, このゲームすごいストーリーが面白くて、英語が難しい
英語の勉強に使っているのだがかれこれ12時間英語漬け
いい英語の教材を買ったかもしれない

場所は

.config/UNDERTALE_linux_steamver

ABC042 D いろはちゃんとマス目

やること

Abstruct

長方形の通れない場所の右の部分と上の部分の2つに分割して、 (0,0) \rightarrow (i, H-A-1) \rightarrow (W, H), i = [B,W]
の経路の数を, すべてのiで計算して, sumすればよい。(上下にわけられた長方形の間の移動は, 1通りの経路のみ)

(0,0) \rightarrow (X,Y) へ移動する経路の数は,

\begin{eqnarray*}
  && {}_{X+Y} \mathrm{C} _X
\end{eqnarray*}
で求めることができる。
これは、 \mathcal{O}(N) で前処理をしておいて, 後で \mathcal{O}(1)で求める

その具体的な方法は http://hos.ac/slides/20130319_enumeration.pdf この記事を参照するとわかりやすい.

ここでは深く言及しない。

SourceCode

const int MOD = 1e9 + 7;
 
// x^n % mod
// O(log n)
template<typename T>
T mod_pow(T x, T n, T mod) {
	T res = 1;
	while (n > 0) {
		if (n & 1) res = res * x & mod;
		x = x*x %mod;
		n >>= 1;
	}
	return res;
}
 
// fact(n)%MOD, invFact(n)%MOD, inv(n)%MOD, nCk
// Constructor O(N)
// other O(1)
template<typename T, int MOD>
class Choose {
public:
	vector<T> fact, inv, invFact;
 
	Choose(T N):fact(N), inv(N), invFact(N){
		inv[1] = 1;
		FOR(i, 2, inv.size()) inv[i] = MOD - (MOD / i) * inv[MOD%i] % MOD;
		fact[0] = invFact[0] = 1;
		FOR(i, 1, fact.size()) {
			fact[i] = fact[i - 1] * i % MOD;
			invFact[i] = (invFact[i - 1] * inv[i]) % MOD;
		}
	}
 
	//Get nCk
	T operator () (T n, T k) const {
		if (not ((T)0 <= k and k <= n)) return 0;
		return (((fact[n] * invFact[k]) % MOD)*invFact[n - k]) % MOD;
	}
};
 
ll f(ll x , ll y, const Choose<ll, MOD>& c) {
	return c(x + y, x);
}
 
signed int main(void) {
	ll H, W, A, B; cin >> H >> W >> A >> B;
	Choose<ll, MOD> c(H + W);
 
	ll ans = 0;
	FOR(i, B, W + 1) {
		ans += f(H - A - 1, i, c) * f(A - 1, W - 1 - i, c);
		ans %= MOD;
	}
 
	cout << ans << endl;
 
	return 0;
}

天下一プログラマーコンテスト2016 予選A C問題 山田山本問題

要約

A_i < B_i, i=1,2,...,N となるように アルファベットの順番を出力する。
アルファベットの順番は辞書順最小のものを出さないといけない

やること

要約

1. AとBを左端から同じ位置にある文字を1文字ずつ比較して違うタイミングでa->bに有向グラフを張る
2. 明示的に、値が小さい方から取り出すトポロジカルソートを使用する.

  • A>B and A.size() != B.size() and グラフを貼れなかったとき: -1
  • グラフが閉路を含んでいる時: -1
  • 辺を張らなかった頂点も, 孤立した単独の頂点として取り扱う。

トポロジカルソート

有向グラフがすべて一方向を向くように、ノードを並べる。
DFSの方法とKhanのアルゴリズムがある。
トポロジカルソート - Wikipedia

memo
  • Khanのアルゴリズムを実装
  • 今回は, priority_queueでアルファベットが小さい順に取り出していく.
  • 辺の削除はコストが掛かりそうなので, 入力辺の数を数えて, 減らしていきながら数を数えた(∵それぞれの辺は一度しか使用されないため)
  • max(残りの入力辺の数) != 0 ならば, 閉路がある(-1を出力する。)

SourceCode

#include <iostream>
#include <queue>
#include <map>
#include <list>
#include <vector>
#include <string>
#include <limits>
#include <cassert>
#include <fstream>
#include <cstring>
#include <bitset>
#include <iomanip>
#include <algorithm>
#include <functional>
#include <cstdio>
#include <ciso646>
#include <array>
#include <cmath>

using namespace std;

#define FOR(i,a,b) for (int i=(a);i<(b);i++)
#define RFOR(i,a,b) for (int i=(b)-1;i>=(a);i--)
#define REP(i,n) for (int i=0;i<(n);i++)
#define RREP(i,n) for (int i=(n)-1;i>=0;i--)

#define inf 0x3f3f3f3f
#define uinf 0x3f3f3f3f3f3f3f3f
#define CLEAR(a) a = decltype(a)()
#define MP make_pair
#define ALL(a) (a).begin(),(a).end()
#define pii pair<int ,int>
#define pcc pair<char,char>
#define pic pair<int,char>
#define pci pair<char,int>
#define VS vector<string>
#define VI vector<int>
#define VVI vector<vector<int>>
#define DEBUG(x) cout<<#x<<": "<<x<<endl
#define pi 2*acos(0.0)
#define INFILE() freopen("in.txt","r",stdin)
#define OUTFILE() freopen("out.txt","w",stdout)
#define ll long long
#define ull unsigned long long
#define eps 1e-14
#define FIN std::ifstream cin("D:\input.txt")
const int MOD = 1e9 + 7;

// @brief topological sort (Khan's algorithm O(|V|+|E|))
// @param G[node] = {to} (G: adjacency list)
// @return sorted values (if G is closed: {})
template<class T>
vector<T> tsort(const vector<vector<T> >& G) {
	vector<T> res;
	vector<T> in(G.size());
	for (auto &node : G) for (auto &to : node) ++in[to];

	priority_queue<int, vector<int>, greater<int>> que;
	REP(i, in.size()) if (in[i] == 0) que.push(i);

	while (not que.empty()) {
		int n = que.top(); que.pop();
		res.push_back(n);
		for (auto &to : G[n]) if (--in[to] == 0) que.push(to);
	}
	return *max_element(ALL(in)) == 0 ? res : vector<T>();
}

signed int main(void) {
	int N; cin >> N;
	vector<vector<int> > G('z'-'a'+1);
	vector<bool> check('z' - 'a' + 1);
	REP(i, N) {
		string A, B; cin >> A >> B;
		int size = min(A.size(), B.size());
		bool isPush = false;
		REP(j,size) {
			if (A[j] != B[j]) {
				check[A[j] - 'a'] = true;
				check[B[j] - 'a'] = true;
				G[A[j]-'a'].push_back(B[j]-'a');
				isPush = true;
				break;
			}
		}
		if (A > B and A.size() != B.size() and (not isPush)) {
			cout << -1 << endl;
			return 0;
		}
	}

	vector<int> res = tsort<int>(G);

	if (res.size() == 0) cout << -1 << endl;

	else {
		for (auto &a : res) cout << (char)(a + 'a');
		cout << endl;
	}

	return 0;
}

ubuntuでterminalを強化したけどいろいろ躓いたメモ

Motivation

後輩がfishを使っていたので、これは便利そうだということで、そろそろterminalを強化してみようと思った
以下を参考に、強化したのだが、いろいろ躓いたところがあるのでメモを残す
各コマンドのチェックはwhichコマンドで

環境 Ubuntu16.04

qiita.com

Content

brewの代わりにaptを使用する.
oh-my-fishのリポジトリが変わっている

github.com


現在インストール方法はこう

curl -L https://github.com/oh-my-fish/oh-my-fish/raw/master/bin/install | fish
agnosterfishテーマは,
 omf install agnoster

のみで良い

powerline/fontsのリポジトリも変わってる

github.com

tmuxは一応ソースビルド

automakeとlibevent-devが必要

 sudo apt install automake
 sudo apt install libevent-dev

入ったらこれ

 git clone https://github.com/tmux/tmux
 sh autogen.sh
 ./configure && make
 sudo make install
pecoはaptじゃあ入らない
sudo apt install golang
export GOPATH=$HOME/go # .bashrcにも追加しておくこと
export PATH=$PATH:$GOROOT/bin # .bashrcにも追加しておくこと
go get github.com/peco/peco/cmd/peco
fish
omf install peco

その他functionなど参考
qiita.com

powerlineも変わってる

github.com

sudo apt install python-pip
pip install powerline-status==2.4
sudo pip psutil
zコマンドがよくわからない

kurenaif.hatenablog.com

powerlineの下にbranch名などを表示させたい

matsu.teraren.com

dotファイルはこれを参考にした
github.com

ubuntuでfish(oh-my-fish導入済み)でz

タイトル通り ubuntuでfishでzコマンドが使いたい

1. z.shを持ってきます。

 git clone https://github.com/rupa/z
 cp z/z.sh /usr/local/bin/

2. z.shのパスを指定

~/.config/fish/config.fish に 以下を追加

 set -gx Z_SCRIPT_PATH /usr/local/bin/z.sh

3. omf

 omf install z

これでfish上でzコマンドが動きます

4. z-fish (2016-10-29追記)

こいつも導入しておくひつようがあるっぽいです

`git clone`をして, `.config/fish/functions`内に`z.fish`を入れてあげる

github.com

参考文献

github.com
qiita.com