題面使用 openssl enc -aes256 -base64 加密。
U2FsdGVkX1/8kkB98g1dG8Xj90RjkCx5lV3FFbUW4At6UhbCwragHBKknZmcd5R2
BsslkKPSQgKrZquzm5UNOENBOSIiXrCvZJ6UhhpQ02NsLO7W4nN+n78dURC7Njtz
hgTJoyRIHlxWB8EF+TozeifCU82nUbE+wyFDUCeJkDLeM3uE4Cvj51TAxILJN4gk
YJKvkAkVAUliBjjEuMZr3yM2SAGZoeasaLNLo2UGvUC8BLyB78KfnF2QyjHFVncg
這道題實際上是 D 題
首先,注意到每一位對於答案的貢獻是獨立的,如 01100 進化 次後結果和 01000 進化 次異或上 00100 進化 次相同。所以可以單獨考慮每一位對於答案的貢獻,當然,如果某一位為 0 的話不對答案產生貢獻。故我們只考慮只有一個 1 的情況。
我們先假設這個串的長度是無限的,永遠不會達到邊界。列舉一個長度為 的 +- 串,遇到 + 表示左移,遇到 - 表示右移,總共有 種可能的貢獻。設有 個 +, 個 -,則對 這個位置做貢獻。反過來,對於 這個位置做貢獻的就有 個。當 時做的貢獻互相抵消,不做貢獻。由盧卡斯定理,
當且僅當 ,即 是 的子集時, 產生貢獻。我們可以把 二進位制拆分,每次處理進化 步的情況,這樣產生貢獻的 只有 和 ,總共進化 次。
接下來我們考慮怎麼處理這個到達邊界的情況。我們構造一個環:
a_0, a_1, a_2, a_3, ... a_n-1,
0, 0,
a_0, a_1, a_2, a_3, ... a_n-1,
考慮進化一次。對於所有的 到 ,結果和對原串相同,對於 和 ,由於旁邊是 ,結果也和原串相同。對於 ,由於左右兩邊相同,結果仍然為 。故對於這樣的環,進化的結果和對原序列的結果相同。這樣就完美解決了到達邊界的問題。
程式碼:
int main()
{
long long T;
int N;
scanf("%lld%d", &T, &N);
std::vector<bool> a(N);
for (int i = 0; i < N; i++) {
int x;
scanf("%1d", &x);
a[i] = x;
}
int n = N * 2 + 2;
std::vector<bool> b;
b.reserve(n);
b.insert(b.end(), a.begin(), a.end());
b.push_back(0);
b.insert(b.end(), a.rbegin(), a.rend());
b.push_back(0);
while (T) {
auto t = T & -T;
T -= t;
t %= n;
std::vector<bool> c(n);
for (int i = 0; i < n; i++) {
if (!b[i]) continue;
c[(i + t) % n] = !c[(i + t) % n];
c[(i + n - t) % n] = !c[(i + n - t) % n];
}
b = c;
}
for (int i = 0; i < N; i++) {
printf("%d", (int)b[i]);
}
printf("\n");
}