[JZOJ5952] 凱旋而歸 ([BZOJ 5092]【Lydsy1711月賽】分割序列)【高維字首和】【DP】
阿新 • • 發佈:2018-12-21
Description
對於一個長度為m的序列a,記
給你一個長度為n的序列a,依次輸出
的f值
Solution
我們先將序列a做一次字首異或,記
那麼
從高到低考慮
的二進位制位,如果
這一位為1,那麼j無論怎麼選這一位對總和貢獻都是
如果這一位為0,那麼就要看
的這一位,如果為1那麼就有
的貢獻,否則貢獻就是0
問題就轉化成我們需要選擇一個j,使得 在 為0的更的高位要儘可能為1,為1的位可以任意,然後按位確定答案。
問題就在於這個為1的位可以任意。我們很難用一個Trie來維護,因為碰到1需要兩邊都一起跑。
考慮預處理
預處理狀態
,S為一個二進位制狀態,一開始S的意義就是S這個二進位制數在序列中最早出現的位置,直接掃一遍存進來即可。
為了滿足我們為1的位可以任意取,我們修改
的定義,
表示所有二進位制數
在序列中最早出現的位置,
,或者說
,即S的某一位為0實際上T可以為0或1。
這個動態維護比較難,我們先掃一遍存進來,然後做DP,列舉每一個為0的位轉移。
本質上,這是一個高維字首和的過程。
現在查詢答案就好查了,我們從高位到低位掃 ,前面已經確定好的不變,然後看這一位是0還是1,是0就判斷是否存在 S’前面都滿足且這一位為1,然後直接統計即可。
(其實用Trie也是可以的,我們先離線將Trie建出來,然後1的兒子不變,0的兒子合併1的兒子狀態,本質與上面相同)
複雜度
Code
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 460005
using namespace std;
int a[N],n,f[1048576],cf[21],ans[N];
int main()
{
cin>>n;
a[0]=0;
cf[0]=1;
fo(i,1,20) cf[i]=cf[i-1]<<1;
memset(f,107,sizeof(f));
fo(i,1,n)
{
scanf("%d",&a[i]),a[i]^=a[i-1];
f[a[i]]=min(f[a[i]],i);
}
fod(j,cf[20]-1,0)
fo(i,0,19) if(!(j&cf[i])) f[j]=min(f[j],f[j^cf[i]]);
fo(i,1,n)
{
int s=0,v=0;
fod(j,19,0)
{
if(a[i]&cf[j]) s+=cf[j];
else
{
v^=cf[j];
if(f[v]<=i) s+=cf[j+1];
else v^=cf[j];
}
}
printf("%d\n",s);
}
}