ARC043 D問題 引っ越し
これは難しい…難しすぎる…
1e6オーダーの家に1e3オーダーの家族(1~100人)を適当に入れて、住民の距離(任意の二人の組み合わせの距離の総和)をMAXにせよ、という問題。
スライドが神がかるほどわかりやすいのでこれだけ見ればおk
AtCoder Regular Contest 043 解説
考え方としては、まず任意の二人の組み合わせの距離とかいう巨大な計算量のものから、それらを家間の距離の総和と考えてみる。
家間の距離は、(辺の左の家の総人数)x(辺の右の家の総人数)で表されることがわかり、しかも左の家の総人数+右の家の総人数=constなので、つまり辺の左の家の総人数だけ考えればいいじゃん!ということになる。
次に、それら家族を並び替える方法を考える、これには定石として隣同士をswapしていく、という方法がありえる。バブルソートもそうだよね。するとなんと、a b cと家族が並んでいるとき、a < b > c ならばbをaかcどちらと交換しても最終的な結果が増大することがわかる。つまり、最適な並び替えにおいてa < b > cという並びはありえない!つまり a >= b <= cとなっている!そしてこのような並び替えの最適値を求めるには定石として、dp[i][x]を大きい方からi番目まで見た時m左側の合計がxだったときの値の最大値、というようにしてdpをする方法がある!
なるほどなるほどー実装は確かに簡単だが、a >= b <= cまでの考察が地獄だ…
constexpr int MAXM = 1010; constexpr int INF = 1e9; int S[MAXM]; ll dp[2][MAXM*100]; int main() { cin.tie(0); ios::sync_with_stdio(false); int N, M; ll sum = 0; cin >> N >> M; rep(i, M){ cin >> S[i]; sum += S[i]; } sort(S, S+M); reverse(S, S+M); rep(i, 2) rep(j, MAXM*100) dp[i][j] = -INF; ll nowsum = 0; dp[0][0] = 0; for(int i=0; i<M; i++){ int cur = i&1; int tar = cur^1; for(int j=0; j<=nowsum; j++){ dp[tar][j] = max(dp[tar][j], dp[cur][j]+(nowsum-j)*(sum-(nowsum-j))); dp[tar][j+S[i]] = max(dp[tar][j+S[i]], dp[cur][j]+(ll)j*(sum-j)); } nowsum += S[i]; } ll res = 0; for(int i=0; i<=sum; i++){ res = max(res, dp[M&1][i]+ll(N-M+1)*i*(sum-i)); } cout << res << endl; }
ARC043 C問題 転倒距離
今までで一番WAを出したかもしれない…つらい…
ある数列A,Bが与えられて、それらから転倒距離が同じになる数列Cを出力する問題。
今回はバブルソートと関連づけられるが、あまりバブルソートのイメージで数字の大小を考えるとすごいわかりづらくなる気がする…。
A,Bでそれぞれある数字xをある数字yと入れ替えても、A,B間の転倒距離は変わらない。(だって何個入れ替わってるかだもん…)
そこでAを1,2,3…になるようにA,Bの数字をそれぞれ入れ替えていく。
そうすると、Bをバブルソートしたときの入れ替え回数と同じようにA,B間の転倒距離がわかる。(求め方は蟻本でもslideでも見て)
そうしたら元のAから元のBになるようにバブルソートしていって、転倒距離/2だけ施行したら止めてあげればそれが答え。
ここでめっちゃハマりにハマった…。
ここで言いたいのは元のAから転倒させていって元のBに近づけていくのである。
まず新しいBをバブルソートしていく。まぁBITとか使えばそこそこ綺麗にまとめられる。
そして最初、Aを1,2,3…となるように変形したが、そのオフセット分が存在するので、転倒距離/2だけバブルソートした新しいBを1,2,3…がAになるように変形してあげれば良い。
この最後のとろこで頭がぐちゃぐちゃになってすごい時間を要した…反省…
struct BinaryIndexedTree{ int N; std::vector<int> bit; //1-indexed つまり1~Nを使う BinaryIndexedTree(int n): N(n), bit(n+1, 0){} void add(int a, int w){ for(int x = a; x <=N; x+=x&(-x)) bit[x] += w; } int sum(int a){ //1~aまでsum int ret = 0; for(int x = a; x>0; x-=x&(-x)) ret += bit[x]; return ret; } }; constexpr int N_MAX = 100100; int A[N_MAX], B[N_MAX], rA[N_MAX], nB[N_MAX], rB[N_MAX]; int memo[N_MAX], res[N_MAX]; int main() { cin.tie(0); ios::sync_with_stdio(false); int N; cin >> N; for(int i=1; i<=N; i++) cin >> A[i]; for(int i=1; i<=N; i++) cin >> B[i]; for(int i=1; i<=N; i++) rA[A[i]] = i; for(int i=1; i<=N; i++) nB[i] = rA[B[i]]; for(int i=1; i<=N; i++) rB[nB[i]] = i; BinaryIndexedTree BitB(N); ll changeB = 0; { for(int i=1; i<=N; i++){ changeB += i-1-BitB.sum(rB[i]); BitB.add(rB[i], 1); } } if(changeB%2 != 0){ cout << -1 << endl; return 0; } ll change = changeB/2; BinaryIndexedTree Bit2(N); for(int i=N; i>=1; i--){ memo[i] = Bit2.sum(rB[i]); Bit2.add(rB[i], 1); } //実際にバブルソート int cur = 1; while(true){ if(memo[cur] < change){ change -= memo[cur]; res[cur] = cur; }else{ int j = cur; for (int i = 1; i <= N; i++) { if (nB[i] >= cur) res[j++] = nB[i]; } int index = 0; for (; res[index] != cur; index++); while (change) { if (res[index] < res[index-1]) change--; swap(res[index], res[index-1]); index--; } break; } cur++; } for(int i=1; i<=N; i++){ cout << A[res[i]] << (i == N ? "\n" : " "); } return 0; }
ARC025 D問題 25個の整数
ちょっとずつ残ってるD問題を潰していきますね。
1~25の数字が5x5のマスに並んでいて(0で少し隠れている)、それから連続する3つをとってきたときに昇順または降順にならないように0で隠れているところを埋める方法が何通りあるかを計算する問題
難しい…
でも気づいてしまえば簡単。
普通に穴を埋めていくだけでN!の時間取られてしまうので、どうにかして他の方法を探す。
なるべく規則性が得られる方向へ…
1~25の数字を順に入れていったらどうか、そうすればslideにあるように、Cのケースにならないようにどんどんマスを埋めていけば良い。
さらに、すでに置いた箇所をまとめて管理することもできる。
つまり今回はint dp[1<<25];のような配列を作り、それぞれのbitが、その場所に数字が入っているかを表すようにしてbitDPすれ良い。
正直スライドを見ればだいたい理解できる気がする。まぁいいや。
constexpr ll mod = (ll)1e9+7; int dp[1 << 25]; int main() { cin.tie(0); ios::sync_with_stdio(false); vector<int> v; int fix[26]; memset(fix, -1, sizeof(fix)); rep(i, 25){ int n; cin >> n; if(n == 0){ v.push_back(i); }else{ fix[n] = i; } } memset(dp, 0, sizeof(dp)); dp[0] = 1; for(int i=0; i<(1<<25)-1; i++){ if(dp[i]){ int c = __builtin_popcount(i) + 1; int r = fix[c]; if(r != -1){ int y = r/5, x = r%5; //x,yにcを入れる if((i&(1<<r)) == 0){ if(y > 0 && y < 4 && ((i>>(r-5))^(i>>(r+5)))&1) continue; //slideのCのケース if(x > 0 && x < 4 && ((i>>(r-1))^(i>>(r+1)))&1) continue; //slideのCのケース dp[i|(1<<r)] += dp[i]; if(dp[i|(1<<r)] >= mod) dp[i|(1<<r)] -= mod; } }else{ for(int j : v){ int y = j/5, x = j%5; if((i&(1<<j)) == 0){ if(y > 0 && y < 4 && ((i>>(j-5))^(i>>(j+5)))&1) continue; //slideのCのケース if(x > 0 && x < 4 && ((i>>(j-1))^(i>>(j+1)))&1) continue; //slideのCのケース dp[i|(1<<j)] += dp[i]; if(dp[i|(1<<j)] >= mod) dp[i|(1<<j)] -= mod; } } } } } cout << dp[(1<<25)-1] << endl; }
ARC045 D問題 みんな仲良し高橋君
難しかったが解説を見ればまぁギリギリ理解できた。
参考にしたのは
AtCoder Regular Contest 045 解説
と
橋と関節点, lowlink - Lilliput Steps
xy座標系に点が奇数個あって、それらはxまたはyが同じ座標なら仲良くなれて、ちゃんと全員ふたり組にできるかな?という問題。
まずx座標同士が同じ点、y座標同士が同じ点を結んでグラフにするという発想がなければいけない。
そうすればそれらの連結成分のサイズが偶数個だったら必ずふたり組ずつにできるよね!というのはまぁ直感ではわかるがまぁと言った感じ。
ということで連結成分を列挙して、連結成分のサイズが奇数個のやつだけについて考える(連結成分のサイズが奇数個のやつが2つ以上存在したらひとつ点をとっても無理だしバイバイ。サイズが偶数個のやつから点とったら絶対サイズが奇数の連結成分できちゃうしバイバイ)
ある点をとった時にそれらがどのようなサイズの連結成分に分かれるかが知りたい。これはつまりグラフの関節を見つけてあげて、分かれたそれぞれのグラフがすべてサイズが偶数ならおkということ。グラフの関節を見つけるにはlowlinkというアルゴリズムを用いる。これはslideがめっちょわかりやすい。
lowlinkは橋とか関節点とかを求めるのに重宝するらしいのでとてもお勉強になった。
typedef pair<int, int> P; constexpr int MAX_V = 200001; vector<int> G[MAX_V]; //lowlinkにかけるためのグラフ vector<P> xs[MAX_V]; //xが同じもの同士 vector<P> ys[MAX_V]; //yが同じもの同士 bool used[MAX_V]; int ord[MAX_V]; int low[MAX_V]; int sum[MAX_V]; bool ans[MAX_V]; int Index = 0; int dfs1(int v) { int cnt = 1; used[v] = true; for(auto u : G[v]){ if(!used[u]) cnt += dfs1(u); } return cnt; } // lowlink int dfs2(int v, int pre) { ord[v] = ++Index; low[v] = ord[v]; int sum = 1; bool res = true; for(auto u : G[v]){ if(pre == u) continue; if(ord[u]){ low[v] = min(low[v], ord[u]); }else{ int s = dfs2(u, v); sum += s; low[v] = min(low[v], low[u]); if(pre == -1){ if(s%2 == 1) res = false; }else{ if(low[u]>=ord[v] && s%2 == 1) res = false; } } } ans[v] = res; return sum; } int main() { cin.tie(0); ios::sync_with_stdio(false); int N; cin >> N; rep(i, 2*N+1){ int x, y; cin >> x >> y; x--; y--; xs[x].push_back(P(y, i)); ys[y].push_back(P(x, i)); } //グラフ構築(近い2点だけ取ってきてオーダーを抑える) for(int i=0; i<2*N+1; i++){ // x or y == iのものについて sort(xs[i].begin(), xs[i].end()); sort(ys[i].begin(), ys[i].end()); int M = (int)xs[i].size(); for(int j=0; j<M; j++){ for(int k=j+1; k<min(j+3, M); k++){ G[xs[i][j].second].push_back(xs[i][k].second); G[xs[i][k].second].push_back(xs[i][j].second); } } M = (int)ys[i].size(); for(int j=0; j<M; j++){ for(int k=j+1; k<min(j+3, M); k++){ G[ys[i][j].second].push_back(ys[i][k].second); G[ys[i][k].second].push_back(ys[i][j].second); } } } //連結成分の数を数えていく int link = -1; for(int i=0; i<2*N+1; i++){ if(used[i]) continue; int cnt = dfs1(i); if(cnt%2 == 1){ if(link >= 0){ for(int j=0; j<2*N+1; j++){ cout << "NG" << endl; } return 0; } link = i; } } //lowlink dfs2(link, -1); for(int i=0; i<2*N+1; i++){ cout << (ans[i] ? "OK" : "NG") << endl; } }
lower_boundとupper_boundとbinary_search
もう毎回忘れてほとんど使えないのでメモ
- lower_boundはvalue以上で最初のiteratorを返す
- upper_boundはvalueより大きい最初のiteratorを返す
- binary_searchはvalueがあるかどうかを返す;
どれもソート済みに使ってね。
最後に比較関数を入れたsampleを貼っておこう
typedef pair<int, int> P; int main() { vector<int> A{3,6,7,9,10,19,31}; cout << distance(A.begin(), lower_bound(A.begin(), A.end(), 9)) << endl; cout << distance(A.begin(), upper_bound(A.begin(), A.end(), 9)) << endl; vector<P> B{{2,4}, {5,3}, {5,7}, {6,1}}; auto it = upper_bound(B.begin(), B.end(), P(5, 0), [](const P& l, const P& r)->bool{return l.first < r.first;}); cout << it->first << " " << it->second << endl; }
raspberrypiの設定
死んだときにもう一度一から調べてやるのがダルいので設定をすべて書いていく。(それddしてimageとっておけばいいじゃんとかは言わない)
ちなみに、rapsi modelB+のraspbian-wheezyです。(いやもうraspi2だろとか言わない)
適当にharadukaというuser名を使ってますがそこら辺は読み替えてね。
- raspbianと調べて出てきたimageをDownloadして sudo dd if=raspbian.img of=/dev/mmcblk0 bs=1M でSDに焼く。ちなみにbs=1Mは転送速度でメモリが十分あるなら1Gとかでもいいのでは
- ルータとraspiをethernetケーブルでつなげてping 192.168.0.2~10くらいまでやって出てきたやつにuser:pi pass:raspberryでlogin
- sudo apt-get update && sudo apt-get upgrade
- sudo apt-get install zsh git vim trash-cli
- 固定アドレスにしよう。sudo vim /etc/network/interces で下記のように書き込もう。sudoを忘れていたら!sudo tee % > /dev/nullで保存できるよ
auto lo iface lo inet loopback auto eth0 iface eth0 inet static address 192.168.0.hoge network 192.168.0.0 netmask 255.255.255.0 broadcast 192.168.0.255 gateway 192.168.0.1 dns-nameservers 192.168.0.1
- ちなみに、IPアドレスはネットマスクとのANDによってアドレス部とホスト部に分割される。よくある192.168.0.1/24の24というのは255.255.255.0と同じ意味で、上から24bitがアドレス部であることを示す。ちなみにboardcastアドレスはブロードキャスト通信の送り先となるIPアドレス
- sudo adduser haraduka してログインし直す。
- scp -r .zshrc .vimrc .vim .gitconfig haraduka@192.168.0.hoge:/home/haraduka/
- chsh で/bin/zshにする。
- user:piでログインしなおしてsudo usermod -G sudo haradukaでharadukaにsudo権限を与える。
- ちなみにファイルの権限がpiになってたらchownとかchgrpしてね。
- 昔ssh-keygenしておいたkeyをssh-copy-id haraduka@192.168.0.hogeでraspiに送りつける。
- ログインしなおしてみてsshでログインできてそうならおk
- sudo vim /etc/ssh/sshd_configでPermitRootLoginをno, PasswordAuthenticationをnoにしてsudo service ssh restart。これでuser:piが使えなくなったよすよす。
- sshを強化しておきたいのでsudo apt-get install denyhosts; まぁdenyhostsは多分初期設定で十分。
- dfとかやって見よう。なぜか8GのSDなのに3Gしかねぇ…そんなときはraspi-configでexpand filesystemをクリック!再起動で治るよ。
- 日本語の設定がしたい
- sudo raspi-configを起動して「Innternnatiolisation Options」を選択 -> Change Localeを選択 -> ja_JP.UTF-8をチェックしてデフォルト言語に選択
- sudo apt-get install ttf-kochi-gothic xfonts-intl-japanese xfonts-intl-japanese-big xfont-kaname」
- sudo apt-get install uim uim-anthy」
- echo $LANG ってやってja_UTF-8とか出たら大丈夫っしょ(僕はzshrcでLANG=Cとやっていたのでここでハマった)
ここからはどうでも良い設定
juliusが使いたい。
結構大変だった。基本的には
ラズパイで音声認識をしてみる | うしこlog
通りにやればおkだが、ハマるところとしては、/devを叩く系のところは必ずsudoをつけること。また、juliusを実行する前に
sudo modprobe snd-pcm-oss
とやることかな。
と思ったが神サイトを見つけたぞ
Juliusによる音声認識 | Raspberry Pi 研究室 | 株式会社クーイエガー
Raspberry Pi 2で音声認識・音声合成してみる - karaage. [からあげ]
open_jtalkが使いたい。
以下のサイトが良い。他のサイトは情報が古い。
Open JTalk (テキストを音声へ変換)をインストールし、使ってみる | レンタルサーバー・自宅サーバー設定・構築のヒント
rubyでgpioが叩きたい
sudo apt-get install ruby-ffi cd /var/lib/gems/1.9.1/specifications/ sudo ln -s /usr/share/rubygems-integration/1.9.1/specifications/ffi-1.0.11.gemspec . sudo gem install pi_piper --no-rdoc --no-ri
ARC45 C問題 エックスオア多橋くん
久しぶりに問題解いた気がする。
a xor b = cのとき、a xor c = bであることを使うとまぁ簡単にいく。
と思いきや、オーバーフローで2つほど落ちてた。はぁ。
vector<P> G[100001]; map<ll, ll> mp; void dfs(int now, int pre, int cost) { mp[cost]++; for(auto v : G[now]){ if(v.first != pre){ dfs(v.first, now, cost^v.second); } } } int main() { cin.tie(0); ios::sync_with_stdio(false); int N; ll X; cin >> N >> X; for(int i=0; i<N-1; i++){ int x, y, c; cin >> x >> y >> c; x--; y--; G[x].push_back(P(y, c)); G[y].push_back(P(x, c)); } dfs(0, -1, 0); ll res = 0; for(auto it=mp.begin(); it!=mp.end(); it++){ ll tar = X^it->first; if(it->first == tar){ res += it->second*(it->second-1)/2; }else{ if(it->first < tar) res += it->second*mp[tar]; } } cout << res << endl; }
CEATEC2015に行ってみて
今日、CEATEC2015に行ってきました。正直企業の人とかが多くて結構ドキドキだったんですが、楽しかったですね。
なんか見たもので覚えていて、かつこれ面白い!みたいなものだけピックアップして書きたいと思います。
- landroid
- LED照明技術
- 網膜走査型レーザアイウェア
- MOVERIO BT200
- FUN'IKI
- 46inch transparent display
- XYZプリンティングジャパン
- RoBoHoN
- テスラモーターズ
- ここの車マジでかっこいい。ていうか、内装に超巨大タッチパネルとか、速度とかの表示器がデジタルdisplayだったりとか、ほんと面白い。
- DJI
- マルチコプターを作っている会社。PhantomだとかMatriceだとか。マルチコプターであまり企業で使うような大きなものを見たことなかったのでとても新鮮だった。バッテリー持ちは約40分。風は10MSまで耐えられる設計らしい。何よりカメラがすごい。カメラのぶれをなくすための3軸のジンバルという姿勢を保つような構造が、とてもすごかった。
- ポータブル蓄電システム
- EneTelusという会社のシステム。まぁIoTには確かに蓄電は必要ですわ。結構大きなサーバーくらいのイメージで、5.0kWhで9時間連続使用できるくらいらしい。
まぁずらずら見てきて最後の方はグダっとしましたが、ざっとこんな感じです。電子部品とかはあまり詳しくなかったので、後で調べつつまた書こうかなぁ…。
FT232RLについて
FT232RLのことを少し調べてみて、知らない単語とかを列挙。
- TTL: transistor-transistor-logic。バイポーラトランジスタと抵抗器のみで構成されるデジタル回路の一種。ただ、高集積化と高速化には向かずCMOSに時代を譲った。TTLlevelとCMOSlevelというのがあるが、これは閾値電圧の違いで、CMOSの方が閾値電圧が高い。TTL levelと単体で言うときは、Hが2.0V以上、Lが0.8V以下となるような信号レベルのことを指す。
- CBUS: FT232RLはI/Oピンが4つあるらしくそれらをCBUSという。
- integrated: 集積された
ちょっと色々調べたいのでわかったら追記していこう…