ABC102 D Equal Cut とりあえず真ん中を固定してみる
ABC102 D Equal Cut
問題概要
リンク参照
考察
- 真ん中を固定してみたくなります(典型っぽい)
- PとQ,RとSについては同じくらいの値になっていれば良さそうに感じます(まぁ分からんでもない)
- 同じくらいの値になる境界を探す処理が必要になります→これは累積和を取って尺取りor二分探索すれば良さそう
- 実装します
- とてもバグります(´;ω;`)ウッ…
- 非常に🌲びしい
ソースコード
#include<iostream> #include<vector> #include<algorithm> #include<cctype> #include<utility> #include<string> #include<cmath> #include<cstring> #include<queue> #include<map> #include<set> #define REP(i, n) for(int i = 0;i < n;i++) #define REPR(i, n) for(int i = n;i >= 0;i--) #define FOR(i, m, n) for(int i = m;i < n;i++) #define FORR(i, m, n) for(int i = m;i >= n;i--) #define SORT(v, n) sort(v, v+n); #define VSORT(v) sort(v.begin(), v.end()); #define llong long long #define pb(a) push_back(a) using namespace std; typedef long long int ll; typedef pair<int, int> pii; typedef pair<ll, ll> pll; template<typename T> vector<T> make_v(size_t a) { return vector<T>(a); } template<typename T, typename... Ts> auto make_v(size_t a, Ts... ts) { return vector<decltype(make_v<T>(ts...))>(a, make_v<T>(ts...)); } template<typename T, typename V> typename enable_if<is_class<T>::value == 0>::type fill_v(T& t, const V& v) { t = v; } template<typename T, typename V> typename enable_if<is_class<T>::value != 0>::type fill_v(T& t, const V& v) { for (auto& e : t) fill_v(e, v); } #define ARRAY_MAX 100005 const ll INF = 1e9; const ll MOD = 1e9 + 7; int dx[4] = { 1,0,0,-1 }; int dy[4] = { 0,1,-1,0 }; /******************************************************************************************/ ll sum[200005]; int main() { cin.tie(nullptr); ios::sync_with_stdio(false); int n; cin >> n; vector<int> a(n); for (int i = 0; i < n; i++) { cin >> a[i]; } for (int i = 1; i <= n; i++) { sum[i] = sum[i - 1] + a[i - 1]; } ll ans = 1LL << 60; int left = 1, center = 2,right = 3; while(center < n - 1) { while (left < center) { if (abs(sum[left] - (sum[center] - sum[left])) > abs(sum[left + 1] - (sum[center] - sum[left + 1]))) { left++; } else { break; } } while (right < n) { if (abs(sum[n] - sum[right] - (sum[right] - sum[center])) > abs(sum[n] - sum[right + 1] - (sum[right + 1] - sum[center]))) { right++; } else { break; } } ll mini = min(min(sum[left], sum[center] - sum[left]), min(sum[right] - sum[center], sum[n] - sum[right])); ll maxi = max(max(sum[left], sum[center] - sum[left]), max(sum[right] - sum[center], sum[n] - sum[right])); ans = min(ans, maxi - mini); center++; } cout << ans << endl; return 0; }
01BFSが使える
ABC005 C 器物損壊!高橋君
問題概要
リンク参照
考察
- 壁が来た時には壊して進むことが考えられるので今まで何回壁を壊したのかの情報を持っておきたい
- なるべく壁を壊す回数を少なくして進む方がよさそう
- 壁を壊して進むことは,コスト1の移動に相当し,壁以外を進む場合はコスト0の移動と考えると01BFSが使える
- これを実装して投げたら通った(・∀・)
ソースコード
#include<iostream> #include<vector> #include<algorithm> #include<cctype> #include<utility> #include<string> #include<cmath> #include<cstring> #include<queue> #include<map> #include<set> #include<climits> #define REP(i, n) for(int i = 0;i < n;i++) #define REPR(i, n) for(int i = n;i >= 0;i--) #define FOR(i, m, n) for(int i = m;i < n;i++) #define FORR(i, m, n) for(int i = m;i >= n;i--) #define SORT(v, n) sort(v, v+n); #define VSORT(v) sort(v.begin(), v.end()); #define llong long long #define pb(a) push_back(a) using namespace std; typedef long long int ll; typedef pair<int, int> pii; typedef pair<ll, ll> pll; template<typename T> vector<T> make_v(size_t a) { return vector<T>(a); } template<typename T, typename... Ts> auto make_v(size_t a, Ts... ts) { return vector<decltype(make_v<T>(ts...))>(a, make_v<T>(ts...)); } template<typename T, typename V> typename enable_if<is_class<T>::value == 0>::type fill_v(T& t, const V& v) { t = v; } template<typename T, typename V> typename enable_if<is_class<T>::value != 0>::type fill_v(T& t, const V& v) { for (auto& e : t) fill_v(e, v); } #define ARRAY_MAX 100005 const int INF = INT32_MAX / 3; const ll MOD = 1e9 + 7; int dx[4] = { 1,0,0,-1 }; int dy[4] = { 0,1,-1,0 }; int ans[550][550]; char mp[550][550]; int h, w; int sy, sx, gy, gx; int main() { cin.tie(nullptr); ios::sync_with_stdio(false); for (int i = 0; i < 550; i++) { for (int j = 0; j < 550; j++) { ans[i][j] = INF; } } cin >> h >> w; for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { cin >> mp[i][j]; if (mp[i][j] == 's') { sy = i; sx = j; } if (mp[i][j] == 'g') { gy = i; gx = j; } } } deque<pii> que; que.push_back(pii(sy,sx)); ans[sy][sx] = 0; while (!que.empty()) { pii now = que.front(); que.pop_front(); for (int i = 0; i < 4; i++) { int ny = now.first + dy[i]; int nx = now.second + dx[i]; //範囲内 int cost = 0; if (0 <= ny && ny < h && 0 <= nx && nx < w) { if (mp[ny][nx] == '#') { //壁なら破壊コストがいる cost = 1; } else { cost = 0; } //コストの更新 if (ans[ny][nx] > ans[now.first][now.second] + cost) { ans[ny][nx] = ans[now.first][now.second] + cost; if (cost == 0) { que.push_front(pii(ny, nx)); } else { que.push_back(pii(ny, nx)); } } } } } if (ans[gy][gx] < 3) { cout << "YES" << endl; } else { cout << "NO" << endl; } return 0; }
The 高校数学(組み合わせ)
ABC132 D Blue and Red Balls
問題概要
個の青ボールと個の赤いボールが与えられる.これらのボールを一列に並べる.1回の操作で連続する青いボールは何個でも回収できる.この時,個の青いボールを全て回収するのに回操作を行う並べ方は何通りあるか,その答えをで割った余りを求めよ
制約
考察
- 高校数学の組み合わせ感があるので考える
- i回操作しないといけないということは,青いボールをi個に分断してある必要がある.この組み合わせは,青いボールの間の個数箇所の中から箇所選ぶ組み合わせになるので通り
- これを赤ボールの間の個数と両端の2か所の合計箇所から箇所選んで埋め込めばいいので,この組み合わせは通り
- 青いボールの分断の仕方通りに対して埋め込み方が通りあるので,i回の操作で青ボールを全て取りきることができる並べ方は通りとなるので各iについてこれを計算すればよい
ソースコード
※マクロは使っていないので無視してください
#include<iostream> #include<vector> #include<algorithm> #include<cctype> #include<utility> #include<string> #include<cmath> #include<cstring> #include<queue> #include<map> #include<set> #define REP(i, n) for(int i = 0;i < n;i++) #define REPR(i, n) for(int i = n;i >= 0;i--) #define FOR(i, m, n) for(int i = m;i < n;i++) #define FORR(i, m, n) for(int i = m;i >= n;i--) #define SORT(v, n) sort(v, v+n); #define VSORT(v) sort(v.begin(), v.end()); #define llong long long #define pb(a) push_back(a) using namespace std; typedef long long int ll; typedef pair<int, int> pii; typedef pair<ll, ll> pll; template<typename T> vector<T> make_v(size_t a) { return vector<T>(a); } template<typename T, typename... Ts> auto make_v(size_t a, Ts... ts) { return vector<decltype(make_v<T>(ts...))>(a, make_v<T>(ts...)); } template<typename T, typename V> typename enable_if<is_class<T>::value == 0>::type fill_v(T& t, const V& v) { t = v; } template<typename T, typename V> typename enable_if<is_class<T>::value != 0>::type fill_v(T& t, const V& v) { for (auto& e : t) fill_v(e, v); } #define ARRAY_MAX 100005 const ll INF = 1e9 + 7; const ll MOD = 1e9 + 7; int dx[4] = { 1,0,0,-1 }; int dy[4] = { 0,1,-1,0 }; struct Combination { vector<ll> fact, rfact; Combination(ll sz) : fact(sz + 1), rfact(sz + 1) { fact[0] = 1; for (ll i = 1; i < fact.size(); i++) { fact[i] = fact[i - 1] * i % MOD; } //逆元 rfact[sz] = inv(fact[sz]); for (ll i = sz - 1; i >= 0; i--) { rfact[i] = rfact[i + 1] * (i + 1) % MOD; } } ll inv(ll x) { //modpow return pow(x, MOD - 2); } ll pow(ll x, ll n) { //累乗 ll ret = 1; while (n > 0) { if (n & 1) (ret *= x) %= MOD; (x *= x) %= MOD; n >>= 1; } return (ret); } ll P(ll n, ll r) { //順列 if (r < 0 || n < r) return (0); return (fact[n] * rfact[n - r] % MOD); } ll C(ll p, ll q) { //組み合わせ if (q < 0 || p < q) return (0); return (fact[p] * rfact[q] % MOD * rfact[p - q] % MOD); } ll H(ll n, ll r) { //重複組み合わせ if (n < 0 || r < 0) return (0); return (r == 0 ? 1 : C(n + r - 1, r)); } }; /******************************************************************************************/ int main() { ll n, k; cin >> n >> k; ll red = n - k; ll inside = k - 1; Combination comb(2050); for (ll i = 1; i <= k; i++) { cout << comb.C(k - 1, i - 1)*comb.C(n - k + 1, i) % MOD << endl; } return 0; }
累積XORだけど生えなかった・・・・(´;ω;`)ウッ…
Codeforces Round #571(Div. 2) C Vus the Cossack String
問題概要
2進数の文字列とが与えられる.文字列の連続する部分文字列で,文字列と同じ長さのものと文字列を比較する.この時,文字が異なる位置が偶数個存在する文字列の連続する部分文字列の個数を求める.
制約
文字列とは2進数の文字列
考察
- 愚直解から計算量を落とそうといろいろ考えたけどダメだった・・・・
- 累積系の処理をしておくと良さそうな感じはしたけどそこから何も見えてこなかった(おいおい・・・・)
- (Twitterで累積XORの解法を観測・・・)累積XORをとると何が分かるのか???・・・・・部分文字列の中にある1の個数の奇偶が分かるな~
- の部分文字列と文字列の各bitを比較して違ったら+1して,最後にこれらが偶数なら解に含まれるというのが愚直な方法になる.bitが違ったら+1になる・・・・bitが同じ場合は無視・・・・・これに該当する演算は・・・・・XORならbitが違う場合は1になる・・・・・これか・・・・・(´;ω;`)ウッ…
- 各ビットのXORを取って1になっている部分の個数を数えて偶数になる・・・・これらのXORも0になる感じでは??・・・・・結局全部XOR取った時に0であればいいのか・・・・?
- 文字列部分文字列との全部のXORが0でなんやねん・・・・(ここでサンプルをグッとにらむ)・・・どうやら文字列部分文字列とに含まれる1の個数の奇偶が同じなら良さそう・・・・・・
- 結局1の個数の奇偶で判定できるのか・・・・ならば文字列部分文字列は累積XORで1の個数の奇偶を求めれば良さそうだし,文字列については固定なので愚直にあらかじめ1の個数を数ればよい(ここでようやく解説と同じところまできた)
- ん~XOR気づきたかったね(´;ω;`)ウッ…
ソースコード
※マクロは使っていないので無視してください
#include<iostream> #include<vector> #include<algorithm> #include<cctype> #include<utility> #include<string> #include<cmath> #include<cstring> #include<queue> #include<map> #include<set> #include<tuple> #define REP(i, n) for(int i = 0;i < n;i++) #define REPR(i, n) for(int i = n;i >= 0;i--) #define FOR(i, m, n) for(int i = m;i < n;i++) #define FORR(i, m, n) for(int i = m;i >= n;i--) #define SORT(v, n) sort(v, v+n); #define VSORT(v) sort(v.begin(), v.end()); #define llong long long #define pb(a) push_back(a) using namespace std; typedef long long int ll; typedef pair<int, int> pii; typedef pair<ll, ll> pll; typedef tuple<int, int, int> tiii; template<typename T> vector<T> make_v(size_t a) { return vector<T>(a); } template<typename T, typename... Ts> auto make_v(size_t a, Ts... ts) { return vector<decltype(make_v<T>(ts...))>(a, make_v<T>(ts...)); } template<typename T, typename V> typename enable_if<is_class<T>::value == 0>::type fill_v(T& t, const V& v) { t = v; } template<typename T, typename V> typename enable_if<is_class<T>::value != 0>::type fill_v(T& t, const V& v) { for (auto& e : t) fill_v(e, v); } #define ARRAY_MAX 100005 int dx[4] = { 1,0,0,-1 }; int dy[4] = { 0,1,-1,0 }; const ll MOD = 1e9 + 7; const ll INF = 1e14 + 7; int main() { string s1, s2; int s1_siz = s1.size(); int s2_siz = s2.size(); vector<int> a(s1.size() + 10, 0); for (int i = 1; i <= s1.size(); i++) { a[i] = s1[i - 1] - '0'; } //累積XOR for (int i = 0; i < s1.size(); i++) { a[i + 1] ^= a[i]; } int cnt = 0; for (int i = 0; i < s2.size(); i++) { if (s2[i] == '1') { cnt++; } } cnt %= 2; int ans = 0; for (int i = s2_siz; i <= s1_siz; i++) { int tmp = a[i] ^ a[i - s2_siz]; if (tmp % 2 == cnt) { ans++; } } cout << ans << endl; return 0; }
つなげておいても損がなさそうならつなげておく・・・・
diverta 2019 Programming Contest C AB Substrings
問題概要
個の文字列が与えられる.これらの文字列を好きな順番でつなげて連結し,1つの文字列を作るとき,文字列に含まれる部分文字列"AB"の個数が最大になるようにする.
制約
は英大文字からなる文字列である
考察
- B****Aと****AとB****のものを組み合わせることまでは見えたけどどうつなげるのが最適なのかが見えてこなかった・・・・・・
- これをよく見ると,B****Aを全てつなげておくと1つのB****Aとして見てもよさそうということがわかる(左右のBとAを有効に使いたいので)
- B****Aをつなげて1つの塊として見られたとすると,次は****AとB****をつなげるのだが,ここで2つ場合分けができる.
1.B****Aが0個の場合
2.B****Aが1個以上ある場合
1の場合は****AとB****をつなげるしかないのでとなる.2の場合はB****と****Aのうち1つずつはをB****Aの左右につなげることができるのでこれをまずつなげる.なぜか??これは,B****Aの左右につなげることで,部分文字列"AB"が2つ生成できるためである.その後,余ったものを使ってにより"AB"をつなげる.そして,文字列内のもとから存在している"AB"の個数を加えれば最大となる.
まとめると,
1.B****Aが0個の場合は+ もとから文字列内に存在している"AB"の個数
2.B****Aが1個以上ある場合は,***Aの個数,B****の個数がそれぞれ1個以上存在しているならば,それをつなげる.その後+ もとから文字列内に存在している"AB"の個数を加える
ん~B****Aをつなげられるだけつなげても悪くなることはなさそうということに気づきたかった・・・・難しい・・・・・
精進して次は解けるようにします!!
ソースコード
※マクロは使っていないので無視してください
#include<iostream> #include<vector> #include<algorithm> #include<cctype> #include<utility> #include<string> #include<cmath> #include<cstring> #include<queue> #include<map> #include<set> #define REP(i, n) for(int i = 0;i < n;i++) #define REPR(i, n) for(int i = n;i >= 0;i--) #define FOR(i, m, n) for(int i = m;i < n;i++) #define FORR(i, m, n) for(int i = m;i >= n;i--) #define SORT(v, n) sort(v, v+n); #define VSORT(v) sort(v.begin(), v.end()); #define llong long long #define pb(a) push_back(a) using namespace std; typedef pair<int, int> pii; typedef long long int ll; template<typename T> vector<T> make_v(size_t a) { return vector<T>(a); } template<typename T, typename... Ts> auto make_v(size_t a, Ts... ts) { return vector<decltype(make_v<T>(ts...))>(a, make_v<T>(ts...)); } template<typename T, typename V> typename enable_if<is_class<T>::value == 0>::type fill_v(T& t, const V& v) { t = v; } template<typename T, typename V> typename enable_if<is_class<T>::value != 0>::type fill_v(T& t, const V& v) { for (auto& e : t) fill_v(e, v); } #define ARRAY_MAX 5005 const ll INF = 1e9 + 7; int dx[4] = { 1,0,0,-1 }; int dy[4] = { 0,1,-1,0 }; const ll MOD = 1e9 + 7; int main() { int n; cin >> n; vector<string> a(n); int a_end = 0; int b_begin = 0; int ba = 0; for (int i = 0; i < n; i++) { cin >> a[i]; if (a[i][0] == 'B' && a[i].back() == 'A') { ba++; } else if (a[i].back() == 'A') { a_end++; } else if (a[i][0] == 'B') { b_begin++; } } int cnt = 0; for (int i = 0; i < n; i++) { for (int j = 0; j < a[i].size() - 1; j++) { if (a[i].substr(j, 2) == "AB") { cnt++; } } } int ans = max(ba - 1,0);//baを1つにまとめる if (ba > 0) { if (a_end > 0) { a_end--; ans++; } if (b_begin > 0) { b_begin--; ans++; } } ans += min(a_end, b_begin); cout << ans + cnt << endl; return 0; }
プランクトンサミット参加記
プランクトンサミットに参加してきました!!!
今回のプランクトンサミットではサイボウズ東京様にお邪魔させていただきました。ありがとうございました。眺めが良かったです!!!
おーーーー!!! pic.twitter.com/0Kbj0CTuoM
— ぷにぷに (@puni_kyopro) May 12, 2019
さて、プランクトンサミットには競プロerとして参加しました。プランクトンサミットでは以下のタスクをしようと思って持っていきました。
- ゼミ資料スライド作成(してません)
- 論文読む(してません)
- 競プロをする(しました)
結局競プロしかしていませんが(汗)
まあそれはいいとしてTLよく見る強い方にたくさんお会いできました。けんちょんさんにモフモフされたり
けんちょんさんにモフモフされてる
— ぷにぷに (@puni_kyopro) 2019年5月12日
あっとさんアイコンを書いたり
アイドルがいる!! pic.twitter.com/rPxCXNLMEH
— ぷにぷに (@puni_kyopro) 2019年5月12日
シンヤカトー氏が来たり
シンヤカトー氏が来た
— ぷにぷに (@puni_kyopro) 2019年5月12日
直大さんにお会いして直筆サイン入りAtCoderステッカーをいただいたり(ありがとうございます!)
直大さん直筆サイン!!! pic.twitter.com/4I6GMv89Fx
— ぷにぷに (@puni_kyopro) 2019年5月12日
バチャをやったり・・・・・
ん~楽しすぎた・・・・・
私はオンサイトにいけるほど強くないのでこういう機会でしか競プロerの方々にお会いできないので強くなってオンサイトでお会いできるように今後も競プロの精進を頑張っていきたいと思いました。
各位本当にお世話になりました!!ありがとうございました!!!!
上書き処理は逆順から見るといいな~というお気持ちが頭の中に残っていたけど・・・・・
CPSCO2019 Session3 D Decode RGB Sequence
問題概要
長さのマスがすべて白色で塗られている.各マスは左から順に1,2,....,の番号がついている.このマス目内の連続する3 マスに対し、左から順に赤色・緑色・青色に上塗りするスタンプを次々と押していく.つまり,整数iを選んだ時に,マスには赤,マスには緑,マスに青色を塗る.このときすでにスタンプが押されているマスの上にもスタンプを押すことができて、スタンプを押された各マスの色は新たなスタンプの色に上塗りさる。この操作を白色のマスがなくなるまで繰り返し行う.なお,白色のマスがなくなってからも操作の継続はでき,白色のマスがなくなっていれば任意のタイミングで操作を終了できるものとする.この時,文字列sのような色あいが実現できるか判定する.
制約
は'R','G','B'からなる文字列である
考察
- この問題を見たときにこれを思い出した
- つまり,逆順に見るというイメージをもってみることにすると良さそうだなということに気がついた
- 1番最後の操作の時点で文字列内に必ず"RGB"の部分は存在しているはずなので,部分文字列"RGB"がない場合は"No"になることが分かった.また,操作時には,"RGB"を上から上書きするので文字列の先頭がRであり,かつ文字列の最後がBでない場合も"No"になることが分かった.
- これ以外は"Yes"だろう・・・・・と思っていた
- ・・・・・・・・・が,甘くなかった.まだ"No"になるケースがあった.
- 実は,"RGB"という文字列を上書きするので,"RB"や"GG"というケースが含まれている場合も"No"になるのだ(これに気が付かずに大量にWAを出してしまった・・・・)
- じゃあ"BR"とか"BG"みたいなのはどうなのかという疑問が生まれる(2文字の部分文字列は上記のものだけではないので)のでこれを検証してみる.実はこれらのケースは"RGB G RGB","RGB G RGB","RGB B RGB"のケースのみを考えれば問題ないことがわかる.つまり,文字列"RGB"において,RとBが上書きされた状態として存在するのだ.よってこれらの部分文字列が含まれていても良さそうということがわかる.
- 以上を踏まえると以下の条件を満たしていれば"Yes"になることがわかる.
1.部分文字列"RGB"が含まれている
2.'R'で始まり'B'で終わっている
3.部分文字列"RB","GG"を含まないこと
- これを実装すればよい
ソースコード
※マクロは使っていないので無視してください
#include<iostream> #include<vector> #include<algorithm> #include<cctype> #include<utility> #include<string> #include<cmath> #include<cstring> #include<queue> #include<map> #include<set> using namespace std; #define REP(i, n) for(int i = 0;i < n;i++) #define REPR(i, n) for(int i = n;i >= 0;i--) #define FOR(i, m, n) for(int i = m;i < n;i++) #define FORR(i, m, n) for(int i = m;i >= n;i--) #define VSORT(v) sort(v.begin(), v.end()); #define pb(a) push_back(a) typedef long long ll; typedef pair<int, int> pii; typedef pair<ll, ll> pll; /*************************************************************************/ //多次元配列の確保及び初期化用マクロ template<typename T> vector<T> make_v(size_t a) { return vector<T>(a); } template<typename T, typename... Ts> auto make_v(size_t a, Ts... ts) { return vector<decltype(make_v<T>(ts...))>(a, make_v<T>(ts...)); } template<typename T, typename V> typename enable_if<is_class<T>::value == 0>::type fill_v(T& t, const V& v) { t = v; } template<typename T, typename V> typename enable_if<is_class<T>::value != 0>::type fill_v(T& t, const V& v) { for (auto& e : t) fill_v(e, v); } /*************************************************************************/ #define ARRAY_MAX 1000005 const ll INF = 1e9 + 7; const ll MOD = 1e9 + 7; int main() { ll n; string s; cin >> n >> s; bool ans = true; ans &= (s[0] == 'R'); ans &= (s[n - 1] == 'B'); if (!ans) { cout << "No" << endl; return 0; } ans = false; for (int i = 0; i <= n - 3; i++) { if (i <= n - 3) { if (s[i] == 'R' && s[i + 1] == 'G' && s[i + 2] == 'B') { ans = true; } } if (i <= n - 2) { string tmp = s.substr(i, 2); if (tmp == "RB" || tmp == "GG") { ans = false; break; } } } if (ans) { cout << "Yes" << endl; } else { cout << "No" << endl; } return 0; }