A
, B
, …, Z
には、アルファベット順に、 が割り当てられていて、計算もできます。例えば、A
+ は B
になります。A
+ を出力すればいいとわかります。#include <bits/stdc++.h>
using namespace std;
int main() {
int k;
cin >> k;
for (int i = 0; i < k; ++i) cout << (char)('A' + i);
return 0;
}
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, m;
int ans = 0;
cin >> n >> m;
vector<string> s(n);
for (int i = 0; i < n; ++i) cin >> s[i];
// 全探索
for (int i = 0; i < n; ++i) {
for (int j = i + 1; j < n; ++j) {
bool flag = true; // 参加者 i、参加者 j ペアは全問解けるか
for (int k = 0; k < m; ++k) {
// 二人とも解けない問題があるなら
if (s[i][k] == 'x' && s[j][k] == 'x') {
flag = false; // 全問解けない
break;
}
}
if (flag) ++ans; // 全問解けるなら答えに 1 加算
}
}
cout << ans;
return 0;
}
"
を数え上げる。"
の個数が偶数個の間は、括られた文字でなく、"
の個数が奇数個の間は、括られた文字であるので、"
の個数が偶数個の間、,
を .
に置き換えればいい。#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
string s;
int cnt = 0;
cin >> n >> s;
for (int i = 0; i < n; ++i) {
if (s[i] == '"') { // '"' なら
++cnt; // 数え上げ
} else if (!(cnt & 1) && s[i] == ',') { // '"' が偶数個のとき ',' なら
s[i] = '.'; // '.' にする
}
}
cout << s;
return 0;
}
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
int n, m;
ll ans = 0;
cin >> n >> m;
vector<vector<int>> g(n);
for (int i = 0, u, v; i < m; ++i) {
cin >> u >> v;
--u;
--v;
g[u].push_back(v);
g[v].push_back(u);
}
// 連結グラフごとに BFS
queue<int> que;
vector<int> d(n, -1); // 距離 (未探索なら -1)
vector<int> group(n); // 連結している頂点同士は同じ値
vector<array<int, 2>> cnt; // group ごと、色 (偶奇) ごとの頂点数
int cntG = 0; // 連結グラフの数
for (int i = 0; i < n; ++i) {
if (d[i] != -1) continue; // 探索済みなら continue
cnt.push_back({0, 0}); // group[cntG] を初期化
que.push(i);
d[i] = 0;
while (!que.empty()) {
int v = que.front();
que.pop();
group[v] = cntG;
++cnt[cntG][d[v] & 1]; // 根からの距離の偶奇で色分けしてカウント
for (int child : g[v]) {
if (d[child] == -1) {
d[child] = d[v] + 1;
que.push(child);
} else if (!((d[child] + d[v]) & 1)) { // 色分けに矛盾が起きたら
cout << 0; // 二部グラフでないので、答えは 0
return 0;
}
}
}
++cntG;
}
// 各頂点ごとに加算
for (int i = 0; i < n; ++i) {
ans += n - g[i].size() - cnt[group[i]][d[i] & 1];
}
cout << ans / 2;
return 0;
}
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
// Union-Find
class UnionFind {
vector<int> p;
public:
UnionFind(int n) : p(n, -1) {}
void unite(int x, int y) {
x = find(x);
y = find(y);
if (x == y) return;
if (p[x] < p[y]) {
p[x] += p[y];
p[y] = x;
} else {
p[y] += p[x];
p[x] = y;
}
return;
}
int find(int x) {
if (p[x] < 0) return x;
while (p[p[x]] >= 0) {
p[x] = p[p[x]];
x = p[x];
if (p[x] < 0) return x;
}
return p[x];
}
bool isSame(int x, int y) { return find(x) == find(y); }
int size(int x) { return -p[find(x)]; }
};
// 法 mod での累乗
long long pow(long long n, long long m, long long mod) {
long long ans = 1;
while (m) {
if (m & 1) ans = ans * n % mod;
n = n * n % mod;
m >>= 1;
}
return ans;
}
int main() {
int n, m;
ll ans = 0;
cin >> n >> m;
vector<ll> a(n);
for (int i = 0; i < n; ++i) cin >> a[i];
// 最大全域木を求める用 ((コスト, 辺の 2 頂点) の組)
vector<pair<ll, pair<int, int>>> edge;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < i; ++j) {
edge.emplace_back((pow(a[i], a[j], m) + pow(a[j], a[i], m)) % m,
make_pair(i, j));
}
}
// クラスカル法 (最大全域木) (最小全域木の場合と逆にソートするだけ)
sort(edge.rbegin(), edge.rend()); // 大きい順にソート
UnionFind uni(n);
for (auto [val, uv] : edge) {
auto [u, v] = uv;
if (!uni.isSame(u, v)) { // 連結でないなら
uni.unite(u, v); // 連結する
ans += val; // 辺のコストを加算
}
}
cout << ans;
return 0;
}
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, q;
cin >> n;
// log = floor(log N) + 1 (面倒なら log = 12 とかでも制約は満たせる)
int log = 32 - __builtin_clz(n);
int m = n * log;
cout << m << endl;
// 配列を出力
for (int i = 0; i < log; ++i) {
for (int j = 1; j <= n; ++j) {
cout << j << ' ' << min((j + (1 << i)) - 1, n) << endl;
}
}
cin >> q;
// クエリ処理
for (int i = 0, l, r; i < q; ++i) {
cin >> l >> r;
// 行は floor(log (r - l + 1)) 行目 (0-indexed)
int row = 31 - __builtin_clz(r - l + 1);
// row 行目の幅は 2 の row 乗
// 配列の row 行 l 列目と、row 行 r - 幅 + 1 行目を出力
cout << row * n + l << ' ' << row * n + r - (1 << row) + 1 << endl;
}
return 0;
}
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
// 2 次元配列を上書きして 2 次元累積和を生成
// (※与える v は、v[0][*] = 0, v[*][0] = 0 を満たすものとする (* は任意))
void cumulativeSum2(vector<vector<ll>> &v) {
int h = v.size();
int w = v[0].size();
for (int i = 1; i < h; ++i) {
for (int j = 2; j < w; ++j) {
v[i][j] += v[i][j - 1];
}
}
for (int i = 2; i < h; ++i) {
for (int j = 1; j < w; ++j) {
v[i][j] += v[i - 1][j];
}
}
}
int main() {
int N, K, P;
cin >> N >> K >> P;
vector<vector<vector<vector<ll>>>> dp(N + 1);
// n = 1 のとき
dp[1] = vector<vector<vector<ll>>>(1, vector<vector<ll>>(N + 1, vector<ll>(N + 1)));
for (int i = 1; i <= N; ++i) {
for (int j = 1; j <= N; ++j) {
dp[1][0][i][j] = 1;
}
}
// dp を計算
for (int n = 2; n <= N; ++n) {
// 要素数 n のとき、類似度の範囲は、0 <= k <= n - 1
// n 個目の要素として選べる要素数は、N - n + 1 個
dp[n] = vector<vector<vector<ll>>>(n, vector<vector<ll>>(N - n + 2, vector<ll>(N - n + 2)));
// すべての k について、dp[n - 1][k]を計算
for (int k = 0; k < n - 1; ++k) {
cumulativeSum2(dp[n - 1][k]);
}
// dp[n] の計算
for (int k = 0; k < n; ++k) {
for (int i = 1; i <= N - n + 1; ++i) {
for (int j = 1; j <= N - n + 1; ++j) {
if (k > 0) { // 類似度が 1 増えるパターン
dp[n][k][i][j] += dp[n - 1][k - 1][i][j];
dp[n][k][i][j] +=
dp[n - 1][k - 1][N - n + 2][N - n + 2] - dp[n - 1][k - 1][N - n + 2][j] -
dp[n - 1][k - 1][i][N - n + 2] + dp[n - 1][k - 1][i][j];
}
if (k < n - 1) { // 類似度が変わらないパターン
dp[n][k][i][j] += dp[n - 1][k][i][N - n + 2] - dp[n - 1][k][i][j];
dp[n][k][i][j] += dp[n - 1][k][N - n + 2][j] - dp[n - 1][k][i][j];
}
dp[n][k][i][j] %= P;
}
}
}
}
cout << dp[N][K][1][1];
return 0;
}
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
// Sparse Table
template <typename T>
class SparseTable {
const vector<T>& arr;
const int sz;
vector<vector<T>> table;
void solve() {
table[0].resize(sz);
for (int i = 0; i < sz; ++i) table[0][i] = arr[i];
for (int i = 1, tsz = table.size(); i < tsz; ++i) {
int range = 1 << i, tisz = sz + 1 - range;
table[i].resize(tisz);
range >>= 1;
for (int j = 0; j < tisz; ++j) {
table[i][j] = op(table[i - 1][j], table[i - 1][j + range]);
}
}
}
T op(T a, T b) { return a < b ? a : b; }
public:
SparseTable(const vector<T>& arr) : arr(arr), sz(arr.size()), table(32 - __builtin_clz(sz)) {
solve();
}
// 区間 [L, R) の最小値を取得
T get(int L, int R) {
int lg = 31 - __builtin_clz(R - L);
return op(table[lg][L], table[lg][R - (1 << lg)]);
}
};
// 分割統治法 (区間 [L, R))
ll solve(int l, int r, SparseTable<pair<ll, int>>& minA, vector<ll>& Sb, ll s) {
// 要素が 0 個なら答えは 0
if (r - l == 0) {
return 0;
}
// MIN は区間 [l, r) の最小値、m はその添え字
auto [MIN, m] = minA.get(l, r);
ll ans = 0;
// 区間 [l, m] の大きさが、区間 [m, r) 以下なら
if (m - l < r - m) {
// 区間 [l, m] を固定
for (int i = l; i <= m; ++i) {
// (累積和が右開区間なので) [m + 1, r] の範囲をにぶたん
int ok = m, ng = r + 1;
for (int mid = (ok + ng) / 2; abs(ok - ng) > 1;
mid = (ok + ng) / 2) {
if (Sb[mid] - Sb[i] + MIN <= s)
ok = mid;
else
ng = mid;
}
// (i, m), …, (i, ok - 1) が条件を満たす
ans += ok - m;
}
} else {
for (int i = m + 1; i <= r; ++i) {
// (累積和が左閉区間なので) [l, m] の範囲をにぶたん
int ok = m + 1, ng = l - 1;
for (int mid = (ok + ng) / 2; abs(ok - ng) > 1;
mid = (ok + ng) / 2) {
if (Sb[i] - Sb[mid] + MIN <= s)
ok = mid;
else
ng = mid;
}
// (ok, i), …, (m, i) が条件を満たす
ans += m - ng;
}
}
// [l, m) と [m + 1, r) に分割して再帰
ans += solve(l, m, minA, Sb, s);
ans += solve(m + 1, r, minA, Sb, s);
return ans;
}
int main() {
int n;
ll s;
cin >> n >> s;
// A も B も 0-indexed
// 組 (A_i, i)
vector<pair<ll, int>> a(n);
// B の累積和 (区間 [l, r) の総和は Sb[r] - Sb[l])
vector<ll> Sb(n + 1);
for (int i = 0; i < n; ++i) cin >> a[i].first, a[i].second = i;
for (int i = 1; i <= n; ++i) cin >> Sb[i];
// Sparse Table を構築
SparseTable<pair<ll, int>> minA(a);
// 累積和を計算
for (int i = 1; i <= n; ++i) Sb[i] += Sb[i - 1];
cout << solve(0, n, minA, Sb, s);
return 0;
}