1. 程式人生 > >xj膜你賽27

xj膜你賽27

凱旋而歸

\(n\) 個數,第 \(i\) 個數為 \(a_i\) 。對於一個由 \(n\) 個數組成的序列 \(a\) ,定義它的帥氣值\[f(a)=max\{(a_1xora_2xor...xora_i)+(a_{i+1}xora_{i+2}xor...xora_n)\}\]
現在給出 \(n\) 個數組成的序列 \(a\) ,求計算 \(a\) 的每個字首的帥氣值。

樣例

5
1 2 3 4 5

1
3
6
10
9

資料範圍

\(n\le 456789,0\le a_i\le 10^6\)

分析

首先 \(a_i\) 不是很大,所以考慮到可以裝桶。
我們假設已經處理出了序列 \(a\)

的字首異或和序列 \(x\)
對於一個 \(x_i\) ,我們先把 \(x_r(1\le r<i)\) 裝桶,具體方法是將 \(x_r\) 的每一個含 \(1\) 的子序列都裝桶,如 \(x_r=1010\) (二進位制),則將 \(1010,1000,0010\) 裝桶。裝桶使用dfs+剪枝,否則會 \(\text{Time Limit Exceeded}\) 飛掉。
然後我們設法把當前的字首異或和 \(x_i\) 拆成兩個前面已出現過的異或和,然後統計答案帥氣值。
按位列舉 \(x_i\) ,在第 \(j\) 位上,若 \(x_{i,j}=1\) ,則答案的第 \(j\) 位必為 \(1\)
\(x_{i,j}=0\)
那麼分出的兩個異或和第 \(j\) 位要麼都是 \(1\) 要麼都是 \(0\) ,此時我們需要一個 \(now\) 變數,若 \(now\) 的某一位為 \(1\) ,則表明分出的兩個異或和的的這一位都為 \(1\), 否則那一位為 \(0\) ,我們判斷當前的 \(now\) 是否在桶中出現過,若出現過,兩個異或和第 \(j\) 位就是 \(1\) ,否則是 \(0\)
假設我們有這麼一些數:
1001
0010
1010
1001
1000
程式執行步驟:
1.檢查1001
當前桶中元素:空

位數 \(now\) \(ans\)
1 0000 1000
2 0000 1000
3 0000 1000
4 0000 1001

2.1001,1000,0001裝桶
3.檢查1011
當前桶中元素:1001,1000,0001

位數 \(now\) \(ans\)
1 0000 1000
2 0000 1000
3 0000 1010
4 0000 1011

4.1011,1001,1010,0011,1000,0010,0001裝桶
5.檢查0001
當前桶中元素:1011,1010,1001,1000,0011,0010,0001

位數 \(now\) \(ans\)
1 1000 10000
2 1000 10000
3 1010 10100
4 1010 10101

6.0001裝桶
7.檢查1000
當前桶中元素:1011,1010,1001,1000,0011,0010,0001

位數 \(now\) \(ans\)
1 0000 1000
2 0000 1000
3 0010 1100
4 0011 1110

8.1000裝桶
9.檢查0000
當前桶中元素:1011,1010,1001,1000,0011,0010,0001

位數 \(now\) \(ans\)
1 1000 10000
2 1000 10000
3 1010 10100
4 1011 10110

10.1000裝桶
輸出結果:
9
11
21
14
22

Code

#include<cstdio>
#define maxn 456795
#define get(x,pos) bool((x)&(1<<(pos))) 
using namespace std;
int read(){
    char c=getchar();
    int x=0;
    while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9'){
        x=(x<<3)+(x<<1)+c-'0';
        c=getchar();
    }
    return x;
}
void write(int x){
    if(x>=10)write(x/10);
    putchar(x%10+'0');
}
int b[1050000];
void add(int x){
    if(b[x])return;
    b[x]=1;
    for(int i=20;i>=0;i--){
        if(get(x,i)){
            add(x-(1<<i));
        }
    }
}
int query(int x){
    int now=0,ret=0;
    for(int i=20;i>=0;i--){
        if(get(x,i))ret|=1<<i;
        else if(b[now|(1<<i)]){
            now|=1<<i;
            ret+=1<<(i+1);
        }
    }
    return ret;
}
int main(){
    int n=read(),a,x=0;
    for(int i=1;i<=n;i++){
        a=read();
        x^=a;
        write(query(x));
        putchar('\n');
        add(x);
    }
    return 0;
}