P11008 『STA - R7』異或生成序列 題解

Brilliant11001發表於2024-08-27

提示:這是一篇不是正解的題解。

題目大意很明確,這裡不再重述。

思路:

讓我們充分發揚人類智慧(

根據異或運算的基本性質,若兩個數同時異或會被消掉,比如 \(a\operatorname{xor} b\operatorname{xor} b = a\)

觀察題目,容易發現將 \(b\) 數列異或起來會有很多重複的數,而它們都能消掉。

即:

\[b_1\operatorname{xor}b_2\operatorname{xor}\cdots \operatorname{xor}b_{i - 1} = p_1\operatorname{xor} p_i(i\in [2, n]) \]

再根據異或運算的另一個性質:若 \(a\operatorname{xor}b = c\),則 \(a = b\operatorname{xor}c\),得:

\[p_i = b_1\operatorname{xor}b_2\operatorname{xor}\cdots \operatorname{xor}b_{i - 1}\operatorname{xor} p_1 (i\in [2, n]) \]

也就是說,我們只要知道了一個 \(p_1\),再依次計算就能得到原數列。

我們先維護一個異或的類似字首和的陣列 \(sum\),即 \(sum_i\) 表示數列 \(b\) 的前 \(i\) 個數 \(\operatorname{xor}\) 起來得到的結果,然後欽定一個 \(p_1\) 並判斷是否合法即可。

但這樣的做法是 \(O(n^2)\) 的,考慮最佳化。

發現瓶頸在於找合法 \(p_1\) 的過程,如果能提前否掉一些不合法的選擇就好了。

由於原數列是一個 \(1\sim n\) 的排列,所以不能出現 \(0\),換句話說就是 \(sum_{i - 1}\operatorname{xor} p_1\) 不能為 \(0\),即 \(p_1\ne sum_{i - 1}\)。同時也不能大於 \(n\),和上面如出一轍。

所以開一個桶提前標記 \(p_1\) 不能取的值,剪掉一些情況。

\(\texttt{Code:}\)

#include <cmath>
#include <ctime>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 2000010;

int T, n;
int a[N], sum[N];
int ans[N];
int cnt;
bool st[N];

int main() {
    scanf("%d", &T);
    while(T--) {
        scanf("%d", &n);
        for(int i = 1; i < n; i++) {
            scanf("%d", &a[i]);
            sum[i] = sum[i - 1] ^ a[i];
            st[sum[i]] = true; //標記不能取的值
        }
        for(int p1 = 1; p1 <= n; p1++) {
            if(st[p1]) continue; //玄學剪枝
            ans[1] = p1;
            bool flag = true;
            for(int j = 2; j <= n; j++) {
                int tmp = sum[j - 1] ^ p1;
                if(tmp > n) {
                    flag = false;
                    break;
                }
                ans[j] = tmp;
            }
            if(flag) {
                for(int j = 1; j <= n; j++)
                    printf("%d ", ans[j]);
                puts("");
                break;
            }
        }
        for(int i = 1; i < n; i++) st[sum[i]] = false; //記得最後還原
    }
    return 0;
}

這裡我才剛把不等於 \(0\) 的情況判掉就透過了。(加強資料迫在眉睫)

歡迎大佬給出詳細的時間複雜度的證明或 hack。