vector
とか queue
とか deque
とか。まあ、vector
は「先頭の要素の削除」に かかるので、両方の操作を でできる queue
とか deque
とかを使うといいかな。queue
は配列みたいに a[i]
とかで値を取得できないので、今回の場合、deque
が便利そう。#include <bits/stdc++.h>
using namespace std;
int main() {
int n, k;
cin >> n >> k;
deque<int> a(n);
for (int i = 0; i < n; ++i) {
cin >> a[i];
}
for (int i = 0; i < k; ++i) {
a.pop_front();
a.push_back(0);
}
for (int i = 0; i < n; ++i) {
cout << a[i] << ' ';
}
}
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
int n, k;
cin >> n >> k;
vector<int> a(n);
for (int i = 0; i < n; ++i) {
cin >> a[i];
}
if (k >= n) { // K >= N なら全部 0
for (int i = 0; i < n; ++i) cout << 0 << ' ';
} else { // そうでないなら K+1 ~ N (0-indexed なら K ~ N-1) まで出力 (残りは 0)
for (int i = k; i < n; ++i) cout << a[i] << ' ';
for (int i = 0; i < k; ++i) cout << 0 << ' ';
}
return 0;
}
#include <bits/stdc++.h>
using namespace std;
int main() {
int h, m;
cin >> h >> m;
int M = m; // 最初は m 分からのループ
for (int i = h;; i = (i + 1) % 24) { // 無限ループ (i = h, h + 1, …, 23, 0, …)
int a = i / 10, b = i % 10;
for (int j = M; j < 60; ++j) { // 最初は m それ以外は 0 からスタート
int c = j / 10, d = j % 10;
if (a * 10 + c < 24 && b * 10 + d < 60) {
cout << i << ' ' << j;
return 0; // 無限ループなので答えが見つかったら終了させる
}
}
M = 0; // 以降 0 分からのループ
}
return 0;
}
#include <bits/stdc++.h>
using namespace std;
int main() {
int h, m;
cin >> h >> m;
// 時刻 (60 進数) を 10 進数に変換して 1 重ループに
for (int i = h * 60 + m;; i = (i + 1) % (24 * 60)) { // 無限ループ
int H = i / 60, M = i % 60; // 時刻 (60 進数) に戻す
int a = H / 10, b = H % 10;
int c = M / 10, d = M % 10;
if (a * 10 + c < 24 && b * 10 + d < 60) {
cout << H << ' ' << M;
return 0; // 無限ループなので答えが見つかったら終了させる
}
}
return 0;
}
"Yes"
、違うなら "No"
を出力すれば OK。組を管理するなら連想配列のクラスを使うと便利。#include <bits/stdc++.h>
using namespace std;
int main() {
int n, q;
cin >> n >> q;
// true ならフォローしている
map<pair<int, int>, bool> follow;
// 配列の値が bool 値なので、map を set に置き換えて、
// bool 値を要素の有無に置き換えることも可能
for (int i = 0, t, a, b; i < q; ++i) {
cin >> t >> a >> b;
if (t == 1) {
follow[{a, b}] = true; // a が b をフォローした
} else if (t == 2) {
follow[{a, b}] = false; // a が b をフォロー解除した
} else if (t == 3) {
// 「a が b をフォローしている」かつ「b が a をフォローしている」
// なら "Yes"、違うなら "No"
cout << (follow[{a, b}] && follow[{b, a}] ? "Yes" : "No") << '\n';
}
}
return 0;
}
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
int n, q;
int num; // 操作 1 で代入する値
int reset = -1; // 操作 1 が最後に現れたのは何番目のクエリか
cin >> n;
vector<ll> a(n);
vector<int> updated(n, -1); // 何番目のクエリのときに代入したか
for (int i = 0; i < n; ++i) cin >> a[i];
cin >> q;
for (int i = 0, t, index, add; i < q; ++i) {
cin >> t;
if (t == 1) {
cin >> num; // 代入する値の更新
reset = i; // 操作 1 が最後に現れたときのクエリ番号を更新
} else if (t == 2) {
cin >> index >> add;
--index; // 0-indexed
if (updated[index] < reset) { // 最後に代入したときより後に操作 1 が現れていたなら
a[index] = num; // 代入する
updated[index] = i; // 代入したときのクエリ番号を更新
}
a[index] += add; // 加算
} else if (t == 3) {
cin >> index;
--index; // 0-indexed
if (updated[index] < reset) { // 最後に代入したときより後に操作 1 が現れていたなら
a[index] = num; // 代入する
updated[index] = i; // 代入したときのクエリ番号を更新
}
cout << a[index] << '\n'; // 出力
}
}
return 0;
}
#include <bits/stdc++.h>
using namespace std;
// 2 次元累積和
void CumulativeSum2D(vector<vector<int>> &a) {
int h = a.size(), w = a[0].size();
// 縦に累積和
for (int i = 1; i < h; ++i) {
for (int j = 0; j < w; ++j) {
a[i][j] += a[i - 1][j];
}
}
// 横に累積和
for (int i = 0; i < h; ++i) {
for (int j = 1; j < w; ++j) {
a[i][j] += a[i][j - 1];
}
}
return;
}
int main() {
int H, W, n, h, w;
cin >> H >> W >> n >> h >> w;
// 累積和を取りたいので縦横 1 ずつ余分に取る
vector<vector<vector<int>>> count(
n, vector<vector<int>>(H + 1, vector<int>(W + 1)));
// 累積和用に 1-indexed
for (int i = 1, a; i <= H; ++i) {
for (int j = 1; j <= W; ++j) {
cin >> a;
--a;
++count[a][i][j];
}
}
// 数字ごとに累積和
for (int i = 0; i < n; ++i) {
CumulativeSum2D(count[i]);
}
// 種類数の計算
for (int k = 0; k <= H - h; ++k) {
for (int l = 0; l <= W - w; ++l) {
int ans = 0; // 種類数
for (int num = 0; num < n; ++num) {
// 全区間の和
int areaAll = count[num][H][W];
// 塗りつぶす区間の和
int areaFill = count[num][k + h][l + w] - count[num][k][l + w] -
count[num][k + h][l] + count[num][k][l];
// 塗りつぶしていないマスに num があるなら種類数を 1 加算
if (areaAll - areaFill > 0) ++ans;
}
cout << ans << '\n';
}
}
return 0;
}
#include <bits/stdc++.h>
using namespace std;
// Grundy 数を求めるやつ
// 文字列の数 n、集合 st、最後に選んだ文字列の番号 p、全文字列 s、Grundy 数 grundy
int cal(int n, int st, int p, vector<string> &s, vector<vector<int>> &grundy) {
// Grundy 数を求め終わっていたらそのまま返す
if (grundy[p][st] != -1) return grundy[p][st];
// 全ての文字列を使い切っていたら 0 を返す
if (st == 0) {
grundy[p][st] = 0;
return 0;
}
set<int> num; // 遷移先の Grundy 数
for (int i = 0; i < n; ++i) {
if (st & (1 << i)) { // 文字列 i を使っていないなら
if (st != ((1 << n) - 1)) { // しりとりの最初の手番でないなら
// 最後に選んだ文字列の最後の文字と文字列 i の最初の文字が
// 等しくないなら continue
if (s[p][s[p].length() - 1] != s[i][0]) continue;
}
// 最後に選んだ文字列の番号を i にして、集合から i を抜いて再帰
int k = cal(n, st ^ (1 << i), i, s, grundy);
// 遷移先の Grundy 数に追加
if (num.find(k) == num.end()) num.insert(k);
}
}
// Grundy 数を求める
{
int i = 0;
// 0 から数字の小さい順に遷移先にその数字があるか調べ、
// なければそれを Grundy 数として終了し、それを返す
for (auto itr = num.begin(); itr != num.end(); ++itr) {
if (*itr != i) {
grundy[p][st] = i;
return i;
}
++i;
}
grundy[p][st] = i;
return i;
}
}
int main() {
int n;
cin >> n;
vector<string> s(n);
// Grundy 数
// grundy[最後に選んだ文字列の番号][選んでない文字列の集合]
// Grundy 数を求める前は -1
vector<vector<int>> grundy(n, vector<int>(1 << n, -1));
for (int i = 0; i < n; ++i) cin >> s[i];
// Grundy 数が 0 なら後手必勝、それ以外なら先手必勝
cout << (cal(n, (1 << n) - 1, 0, s, grundy) == 0 ? "Second" : "First");
return 0;
}
#include <bits/stdc++.h>
using namespace std;
// field (カードが存在する区間の集合を [key, value) で管理したもの) の更新
// 更新によって変化した Grundy 数の差分 cmd を返す
// つまり、[更新前の Grundy 数] xor cmd = [更新後の Grundy 数]
// 引数は、操作 x, y、カードが存在する区間の集合 field、Grundy 数 grundy
int updateField(int x, int y, map<int, int> &field, const vector<int> &grundy) {
auto itr = field.upper_bound(x); // カード x が存在する区間の次の区間
--itr; // カード x が存在する区間に移動
// 更新によって変化した Grundy 数の差分
// (itr->second - itr->first は区間の大きさ (カード数))
int cmd = grundy[itr->second - itr->first];
/* カード x が存在する区間 [itr->first, itr->second) を */
/* 区間 [itr->first, x) と 区間 [x + y, itr->second) に分割する [ここから] */
// カードを取り除いた後の右側 (正方向) の区間 [x + y, itr->second) の大きさが 0 でないなら
if (x + y < itr->second) {
field[x + y] = itr->second; // 区間 [x + y, itr->second) の追加
cmd ^= grundy[itr->second - (x + y)]; // Grundy 数の差分を更新
}
// カードを取り除いた後の左側 (負方向) の区間 [itr->first, x) の大きさが 0 なら
if (itr->first == x) {
field.erase(itr); // 区間 [itr->first, itr->second) を消す
} else { // 0 でないなら
itr->second = x; // 区間 [itr->first, itr->second) を 区間 [itr->first, x) に更新
cmd ^= grundy[x - itr->first]; // Grundy 数の差分を更新
}
/* カード x が存在する区間 [itr->first, itr->second) を */
/* 区間 [itr->first, x) と 区間 [x + y, itr->second) に分割する [ここまで] */
return cmd;
}
// 自分のターンの操作を実行する
// 引数は、現在の状態の Grundy 数 cmd、取り除くカード数 y、
// カードが存在する区間の集合 field、Grundy 数 grundy
void op(int cmd, int y, map<int, int> &field, const vector<int> &grundy) {
// Grundy 数を 0 にする操作が可能な区間を探す
auto itr = field.begin();
int size; // 区間の大きさ
int toZero;
// 区間の Grundy 数 grundy[size] を toZero に遷移させると、
// 全体の Grundy 数を 0 にできる
for (; itr != field.end(); ++itr) {
size = itr->second - itr->first;
toZero = cmd ^ grundy[size];
// toZero < [区間の Grundy 数] なら toZero に遷移できるので終了
if (toZero < grundy[size]) break;
}
// 区間の Grundy 数 grundy[size] を操作によって toZero にすると
// 操作後の全体の Grundy 数が 0 になる
assert(itr != field.end()); // そのような区間は必ず存在する
// 操作後のGrundy 数が 0 になるような操作の検索
for (int i = 0; i <= size; ++i) {
if ((toZero ^ grundy[i] ^ grundy[size - i - y]) == 0) { // 見つけたら
cout << itr->first + i << ' ' << y << endl; // 操作方法を出力
// field の更新
// 操作後の Grundy 数は 0 になるので、[updateField の返り値] == cmd
assert(updateField(itr->first + i, y, field, grundy) == cmd);
break;
}
}
}
int main() {
int n, l, r;
int a, b;
cin >> n >> l >> r;
// 「L = R」かつ「N と L の偶奇が一致していない」なら Grundy 数を求める
if (l == r && (n - l) % 2) {
// 連続で k 枚並んでいる状態のときの Grundy 数
vector<int> grundy(n + 1);
// Grundy 数を求める
for (int i = 1; i <= n; ++i) {
if (i - l < 0) continue; // 取り除けないなら grundy[i] == 0;
vector<bool> num(n);
for (int j = 0; j <= i - l; ++j){
num[grundy[j] ^ grundy[i - l - j]] = true;
}
for (; grundy[i] < n; ++grundy[i]) {
if (!num[grundy[i]]) break;
}
}
map<int, int> field; // カードが存在する区間の集合を [key, value) で管理
int cmd = grundy[n]; // 現在の状態の Grundy 数
field[1] = n + 1; // field に区間 [1, n + 1) を追加
if (cmd) { // Grundy 数が 0 でないなら
cout << "First" << endl; // 先手
op(cmd, l, field, grundy); // 自分の操作を実行
} else { // 0 なら
cout << "Second" << endl; // 後手
}
cin >> a >> b;
while (b) {
cmd = updateField(a, b, field, grundy); // 相手の操作を反映
op(cmd, l, field, grundy); // 自分の操作を実行
cin >> a >> b;
}
} else { // 違うなら「真似っこ戦略」
// N と R の偶奇が一致していないなら (r - 1) 枚、一致しているなら r 枚取り除く
int y = r - (n - r) % 2;
// 真ん中を取り除くときの位置
int x = n / 2 - y / 2 + 1;
// 真似っこするときの位置の差分
int range = x + y - 1;
// 先手必勝
cout << "First" << endl;
cout << x << ' ' << y << endl;
cin >> a >> b;
while (b) {
// 真似っこ
if (a >= range) {
cout << a - range;
} else {
cout << a + range;
}
cout << ' ' << b << endl;
cin >> a >> b;
}
}
return 0;
}