E - 暗号変換装置とパスワード認証 / Cipher Conversion Device and Password Authentication 解説 by admin
Gemini 3.0 Flash (Thinking)Overview
This problem asks us to optimize both the construction of a password \(S\) and the order in which it is sent to agents, in order to maximize the number of agents who successfully authenticate. By fixing the sending order, the password \(S\) required for an agent to authenticate at each step is uniquely determined. We derive the solution by combining an exhaustive search over orderings with management of password candidates.
Analysis
1. Sending Order and Permutation Transitions
The number of agents \(N\) and the permutation size \(K\) are both at most 8, which is very small. On the other hand, the password length \(L\) can be up to 200. Let the sending order be \(\sigma = (\sigma_1, \sigma_2, \dots, \sigma_N)\). The device’s permutation \(C^{(k)}\) at step \(k\) is uniquely determined by the composition of permutations \(A\) of agents sent before that step. - \(C^{(1)} = \text{id}\) (identity permutation) - \(C^{(k+1)} = A_{\sigma_k} \circ C^{(k)}\)
2. Uniqueness of Password \(S\)
For agent \(\sigma_k\) to successfully authenticate at step \(k\), it is necessary that \(C^{(k)}_{S_j} = T_{\sigma_k, j}\) holds for each \(j \in \{1, \dots, L\}\). Since \(C^{(k)}\) is a permutation (bijection), letting \(D^{(k)}\) be its inverse permutation, each element of \(S\) is uniquely determined as \(S_j = D^{(k)}_{T_{\sigma_k, j}}\).
If this \(S\) does not contradict Takahashi’s memo \(B\) (i.e., if \(B_j \neq 0\) then \(S_j = B_j\)), then this \(S\) is a valid password candidate.
3. Optimization Strategy
There are \(N! = 8! = 40320\) possible sending orders \(\sigma\). For each order, if we choose one agent whose authentication we want to succeed (let that step be \(k\)), the password \(S\) is uniquely determined. Using that \(S\), whether authentication succeeds at another step \(m\) within the same order \(\sigma\) can be checked simply by verifying whether \(C^{(m)}_{S_j} = T_{\sigma_m, j}\) holds for all \(j\).
Algorithm
Indexing Permutations: Enumerate all \(K!\) permutations and assign an ID to each. Also, precompute the permutation ID that results from composing a given permutation \(C\) with agent \(i\)’s permutation \(A_i\).
Extracting Password Candidates: For every pair \((i, C)\) of each agent \(i\) and every possible device state (permutation \(C\)), compute the password \(S\) required for successful authentication.
- If \(S\) does not contradict memo \(B\), save that \(S\).
- Sort all saved \(S\) values in lexicographic order, remove duplicates, and assign unique IDs. This allows us to replace password comparisons with integer ID comparisons.
Exhaustive Search Over Sending Orders: Enumerate all \(N!\) agent orderings using
next_permutationor similar.- For each ordering, sequentially compute the permutation \(C^{(k)}\) at each step \(k\).
- If agent \(\sigma_k\) can be authenticated at step \(k\) (i.e., \(S\) exists), record the ID of that \(S\).
- For each recorded \(S\) ID, count how many agents successfully authenticate in that ordering.
Updating the Result: Update the maximum number of people, and when the count is the same, keep the one with the smallest \(S\) ID (= lexicographically smallest).
Complexity
Precomputation: \(O(N \cdot K! \cdot K)\) (transitions) and \(O(N \cdot K! \cdot L)\) (creating candidates \(S\))
Sorting: \(O((N \cdot K!) \log (N \cdot K!) \cdot L)\)
Exhaustive search: \(O(N! \cdot N^2)\)
- When \(N=8, K=8, L=200\), we have \(N! = 40320, K! = 40320\).
- \(N! \cdot N^2 \approx 4 \times 10^4 \cdot 64 \approx 2.5 \times 10^6\), which is sufficiently fast.
Time complexity: \(O(N \cdot K! \cdot (K+L) + (N \cdot K!) \log (N \cdot K!) \cdot L + N! \cdot N^2)\)
Space complexity: \(O(N \cdot K! \cdot L)\) (storage of candidate passwords)
Implementation Notes
Hashing permutations: Since \(K \le 8\), permutations can be converted to integers by treating them as base-8 numbers, enabling fast ID management using arrays.
Lexicographically smallest \(S\): When the maximum number of people is 0, the lexicographically smallest answer is obtained by setting all undetermined parts of memo \(B\) to the smallest value (1).
ID management: When converting candidate \(S\) values to IDs, maintain a list like
unique_Usso that the original \(S\) values can be recovered.Source Code
#include <iostream>
#include <vector>
#include <numeric>
#include <algorithm>
using namespace std;
// Array to map permutations to indices based on their base-8 representation
// Since K <= 8, 8^8 = 16777216 is sufficient.
int perm_to_idx_arr[16777216];
struct TempU {
int i, cp;
vector<uint8_t> U;
};
int main() {
// Faster I/O
ios::sync_with_stdio(false);
cin.tie(nullptr);
int N, K, L;
if (!(cin >> N >> K >> L)) return 0;
// Read templates and information for each agent
vector<int> B(L);
for (int i = 0; i < L; ++i) {
cin >> B[i];
B[i]--; // Convert to 0-indexed, -1 means unassigned
}
vector<vector<int>> T(N, vector<int>(L));
vector<vector<int>> A(N, vector<int>(K));
for (int i = 0; i < N; ++i) {
for (int j = 0; j < L; ++j) {
cin >> T[i][j];
T[i][j]--; // 0-indexed
}
for (int j = 0; j < K; ++j) {
cin >> A[i][j];
A[i][j]--; // 0-indexed
}
}
// Generate all permutations of {0, ..., K-1}
vector<vector<int>> all_perms;
vector<int> p(K);
iota(p.begin(), p.end(), 0);
int K_fact = 0;
for (int i = 0; i < 16777216; ++i) perm_to_idx_arr[i] = -1;
do {
all_perms.push_back(p);
int id = 0;
for (int x : p) id = (id << 3) | x;
perm_to_idx_arr[id] = K_fact++;
} while (next_permutation(p.begin(), p.end()));
// Pre-calculate the transformation update: C' = A_i ∘ C
vector<vector<int>> next_C_idx(N, vector<int>(K_fact));
for (int i = 0; i < N; ++i) {
for (int cp = 0; cp < K_fact; ++cp) {
int next_id = 0;
for (int x = 0; x < K; ++x) {
next_id = (next_id << 3) | A[i][all_perms[cp][x]];
}
next_C_idx[i][cp] = perm_to_idx_arr[next_id];
}
}
// Pre-calculate possible passwords U for each (agent i, transformation cp)
vector<TempU> all_consistent;
all_consistent.reserve(N * K_fact);
for (int i = 0; i < N; ++i) {
for (int cp = 0; cp < K_fact; ++cp) {
const auto& C = all_perms[cp];
bool ok = true;
// Consistency check: S_j must satisfy C(S_j) = T_{i,j}.
// If S_j is fixed to B_j, then C(B_j) = T_{i,j} must hold.
for (int j = 0; j < L; ++j) {
if (B[j] != -1 && C[B[j]] != T[i][j]) {
ok = false;
break;
}
}
if (ok) {
vector<int> D(K);
for (int x = 0; x < K; ++x) D[C[x]] = x; // D is the inverse of C
vector<uint8_t> U(L);
for (int j = 0; j < L; ++j) U[j] = (uint8_t)D[T[i][j]];
all_consistent.push_back({i, cp, U});
}
}
}
// Sort consistent passwords to assign unique IDs based on lexicographical order
sort(all_consistent.begin(), all_consistent.end(), [](const TempU& a, const TempU& b) {
return a.U < b.U;
});
vector<vector<int>> consistent_id(N, vector<int>(K_fact, -1));
vector<vector<uint8_t>> unique_Us;
unique_Us.reserve(all_consistent.size());
int current_id = -1;
for (int i = 0; i < (int)all_consistent.size(); ++i) {
if (i == 0 || all_consistent[i].U != all_consistent[i - 1].U) {
current_id++;
unique_Us.push_back(all_consistent[i].U);
}
consistent_id[all_consistent[i].i][all_consistent[i].cp] = current_id;
}
// Iterate through all permutations of agents to find the maximum authentication count
int max_auth = 0;
int best_S_id = -1;
vector<int> agent_p(N);
iota(agent_p.begin(), agent_p.end(), 0);
// Initial identity permutation index
int id_val = 0;
for (int x = 0; x < K; ++x) id_val = (id_val << 3) | x;
int id_perm_idx = perm_to_idx_arr[id_val];
do {
int curr_C = id_perm_idx;
vector<int> agent_ids(N);
for (int k = 0; k < N; ++k) {
agent_ids[k] = consistent_id[agent_p[k]][curr_C];
curr_C = next_C_idx[agent_p[k]][curr_C];
}
for (int k = 0; k < N; ++k) {
int id = agent_ids[k];
if (id == -1) continue;
// Avoid redundant counts for the same ID in one agent permutation
bool already_checked = false;
for (int m = 0; m < k; ++m) {
if (agent_ids[m] == id) {
already_checked = true;
break;
}
}
if (already_checked) continue;
int cnt = 0;
for (int m = 0; m < N; ++m) {
if (agent_ids[m] == id) cnt++;
}
if (cnt > max_auth) {
max_auth = cnt;
best_S_id = id;
} else if (cnt == max_auth && max_auth > 0) {
// Since IDs represent lexicographical rank, pick the smaller ID
if (id < best_S_id) {
best_S_id = id;
}
}
}
} while (next_permutation(agent_p.begin(), agent_p.end()));
// Output the results
cout << max_auth << "\n";
if (max_auth == 0) {
// If no agent can be authenticated, the smallest S is B with unknown values as 1
for (int i = 0; i < L; ++i) {
cout << (B[i] == -1 ? 1 : B[i] + 1) << (i == L - 1 ? "" : " ");
}
cout << "\n";
} else {
// Output the lexicographically smallest S achieving max_auth
for (int i = 0; i < L; ++i) {
cout << (int)unique_Us[best_S_id][i] + 1 << (i == L - 1 ? "" : " ");
}
cout << "\n";
}
return 0;
}
This editorial was generated by gemini-3-flash-thinking.
投稿日時:
最終更新: