結果
| 問題 |
No.3290 Encrypt Failed, but Decrypt Succeeded
|
| コンテスト | |
| ユーザー |
titia
|
| 提出日時 | 2025-10-08 04:31:43 |
| 言語 | C++23 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
WA
|
| 実行時間 | - |
| コード長 | 9,409 bytes |
| コンパイル時間 | 5,559 ms |
| コンパイル使用メモリ | 395,292 KB |
| 実行使用メモリ | 21,456 KB |
| 最終ジャッジ日時 | 2025-10-08 04:31:51 |
| 合計ジャッジ時間 | 7,797 ms |
|
ジャッジサーバーID (参考情報) |
judge5 / judge3 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 3 |
| other | AC * 25 WA * 2 |
ソースコード
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <stdexcept>
#include <cmath>
// Nの最大値 300,000 に対応
const int MAX_DP_SIZE = 300010;
// Kの最大値 10^18 に対応するための定数
const long long K_LIMIT = 1000000000000000000LL; // 10^18
// Kを超えるデコード総数を表す値 (K + 1)。これ以上の値は K を超えるとみなす。
const long long OVERFLOW_VAL = K_LIMIT + 1;
// DPテーブルをグローバルで定義(再帰関数からアクセスするため)
std::vector<long long> DP(MAX_DP_SIZE);
std::vector<long long> DP2(MAX_DP_SIZE);
std::string X_str; // PythonのXに対応する前処理済み文字列
// calc2の前方宣言
long long calc2(int x);
/**
* @brief X[x:]以降の文字列をデコードする方法の総数を計算します(DP/メモ化再帰)。
* デコード総数が OVERFLOW_VAL (K+1) を超える場合は、OVERFLOW_VAL でクリップされます。
* @param x 現在のXのインデックス
* @return long long デコード総数
*/
long long calc(int x) {
if (DP[x] != -1) {
return DP[x];
}
// ベースケース: インデックスが末尾または末尾+1の場合(1通り)
if (x >= X_str.length() - 1) {
DP[x] = 1;
return 1;
}
// X[x]X[x+1] の2文字を組み合わせた文字列
std::string two_char_str;
two_char_str += X_str[x];
two_char_str += X_str[x + 1];
// Case 1: 特殊文字 ('j' or 't') が含まれる場合
if (two_char_str.find('j') != std::string::npos || two_char_str.find('t') != std::string::npos) {
// X[x]が特殊文字の場合、1文字として確定。次は X[x+1:] を calc(x+1) 通り
long long ANS = calc(x + 1);
DP[x] = ANS;
return ANS;
}
// 2文字を整数に変換
int a = 0;
try {
a = std::stoi(two_char_str);
} catch (...) {
// 変換エラーは通常発生しないはずだが、発生時は1文字デコードのみ
long long ANS = calc(x + 1);
DP[x] = ANS;
return ANS;
}
// Case 2: 2文字デコードが可能な場合 (11-19, 21-26)
if ((a >= 11 && a <= 19) || (a >= 21 && a <= 26)) {
// Pythonのロジックを忠実に再現: calc(x+2)*2 + calc2(x+1)
// オーバーフロー対策: K_LIMIT (10^18) を超える場合、OVERFLOW_VAL にクリップ
long long term1 = calc(x + 2);
// calc(x+2) * 2 が OVERFLOW_VAL を超えるかチェック
if (term1 >= OVERFLOW_VAL / 2) {
term1 = OVERFLOW_VAL;
} else {
term1 *= 2;
}
long long term2 = calc2(x + 1);
long long ANS = term1;
// term1 + term2 が OVERFLOW_VAL を超えるかチェック
if (term2 >= OVERFLOW_VAL - term1) {
ANS = OVERFLOW_VAL;
} else {
ANS += term2;
}
DP[x] = ANS;
return ANS;
} else {
// Case 3: 1文字デコードのみが可能な場合
long long ANS = calc(x + 1);
DP[x] = ANS;
return ANS;
}
}
/**
* @brief X[x]X[x+1]を2文字としてデコードし、残りのX[x+2:]をデコードするパターン数を計算します。
* 総数は calc(x+2) に等しく、OVERFLOW_VAL でクリップされます。
* @param x 現在のXのインデックス
* @return long long X[x]X[x+1]を2文字としてデコードするパターン数
*/
long long calc2(int x) {
if (DP2[x] != -1) {
return DP2[x];
}
// ベースケース: 2文字デコード不可能
if (x >= X_str.length() - 1) {
DP2[x] = 0;
return 0;
}
std::string two_char_str;
two_char_str += X_str[x];
two_char_str += X_str[x + 1];
// Case 1: 特殊文字が含まれる場合。2文字デコード不可。
if (two_char_str.find('j') != std::string::npos || two_char_str.find('t') != std::string::npos) {
DP2[x] = 0;
return 0;
}
// 2文字を整数に変換
int a = 0;
try {
a = std::stoi(two_char_str);
} catch (...) {
DP2[x] = 0;
return 0;
}
// Case 2: 2文字デコードが可能な場合 (11-19, 21-26)
if ((a >= 11 && a <= 19) || (a >= 21 && a <= 26)) {
// X[x]X[x+1]を2文字デコードし、残りの X[x+2:] を calc(x+2) 通り
// calc(x+2) の結果は既に OVERFLOW_VAL でクリップされている
long long ANS = calc(x + 2);
DP2[x] = ANS;
return ANS;
} else {
// Case 3: 2文字デコード不可
DP2[x] = 0;
return 0;
}
}
int main() {
// 高速な入出力設定
std::ios_base::sync_with_stdio(false);
std::cin.tie(NULL);
// Nはint (300,000) で十分
int N;
// Kはlong long (10^18) が必要
long long K;
std::string S;
// 入力
if (!(std::cin >> N >> K)) return 0;
if (!(std::cin >> S)) return 0;
// K=1 の特殊処理
if (K == 1) {
std::string ANS;
for (char c : S) {
// '1' -> 'a', '9' -> 'i' (chr(int(S[i])+96) に対応)
if (c >= '1' && c <= '9') {
ANS += static_cast<char>(c - '0' + 'a' - 1);
}
}
std::cout << ANS << "\n";
return 0;
}
// 文字列 X の生成 (前処理)
X_str.clear();
int ind_s = 0;
while (ind_s < N) {
if (ind_s + 1 < N && S[ind_s + 1] == '0') {
// 'x0'の形を特殊文字に変換
if (S[ind_s] == '1') {
X_str.push_back('j'); // 10 -> j
ind_s += 2;
} else if (S[ind_s] == '2') {
X_str.push_back('t'); // 20 -> t
ind_s += 2;
} else {
// 30, 40などはデコードできないため、元のPythonコードのロジックに従い1文字として処理
// 3, 4, ... を X_str に追加
X_str.push_back(S[ind_s]);
ind_s++;
}
} else {
X_str.push_back(S[ind_s]);
ind_s++;
}
}
int X_len = X_str.length();
if (X_len == 0 && N > 0) return 0;
// N=300000に対してX_lenも最大300000程度になる
// DPテーブルの初期化と設定
// Nの最大値に合わせて resize/assign する
DP.assign(X_len + 10, -1);
DP2.assign(X_len + 10, -1);
// calc(X_len) (空文字列) は1通り
DP[X_len] = 1;
DP2[X_len] = 0;
// K番目の文字列の探索
std::string ANS_str;
long long K_temp = K;
int ind = 0;
while (ind < X_len) {
// バイナリサーチ: calc(mid) >= K_temp を満たす最大の mid を探す
// OK: デコード総数 >= K_temp
// NG: デコード総数 < K_temp
int OK = ind;
int NG = X_len;
while (NG > OK + 1) {
int mid = OK + (NG - OK) / 2;
long long score = calc(mid);
if (score >= K_temp) {
OK = mid;
} else {
NG = mid;
}
}
// ここでの OK は X[OK]X[OK+1] を2文字デコードするグループの開始位置
// X[OK]を1文字デコードするパターン群 calc(OK+1) 通りをスキップ
if (OK + 1 <= X_len) {
// K_temp は K_LIMIT を超えない値として保持されているので、オーバーフローの心配はない
K_temp -= calc(OK + 1);
} else {
// このパスはバイナリサーチのロジック上、通らないはず
break;
}
// X[ind]からX[OK-1]までは1文字デコードが確定
for (int i = ind; i < OK; ++i) {
if (X_str[i] == 'j' || X_str[i] == 't') {
ANS_str += X_str[i];
} else {
ANS_str += static_cast<char>(X_str[i] - '0' + 'a' - 1);
}
}
// X[OK]X[OK+1] を2文字でデコード
std::string two_char_val;
if (OK + 1 < X_len) {
two_char_val += X_str[OK];
two_char_val += X_str[OK + 1];
} else {
// このパスはバイナリサーチのロジック上、通らないはず
break;
}
// 2文字の数字を整数に変換
int decoded_val = 0;
try {
decoded_val = std::stoi(two_char_val);
} catch (...) {
// デコードエラーが発生した場合、処理を中断
return 0;
}
// 変換してANSに追加 (例: 11 -> k)
ANS_str += static_cast<char>(decoded_val + 'a' - 1);
ind = OK + 2; // 次の開始インデックス
if (K_temp == 1) {
// 残りの文字列を1文字ずつデコード(これがK番目のデコードパターンになる)
for (int i = ind; i < X_len; ++i) {
if (X_str[i] == 'j' || X_str[i] == 't') {
ANS_str += X_str[i];
} else {
ANS_str += static_cast<char>(X_str[i] - '0' + 'a' - 1);
}
}
break;
}
}
// 結果を出力
std::cout << ANS_str << "\n";
return 0;
}
titia