您的位置:首页 > 其它

【HDU - 5845】Best Division(xor-trie、01字典树、dp)

2017-07-03 17:38 393 查看
BUPT2017 wintertraining(15) #7E

题意

把数组A划分为k个区间,每个区间不超过L长度,每一个区间异或和之和为S。现在求:S不超过X,区间个数的最大值。

且A是这样给你的:A[1], P, Q。A[i]=(A[i-1]*P+Q)%M。M为\(2^{28}\)。

题解

容易想到dp, dp[i] :前 i 个数最多划分多少个区间。s[i]为前缀异或和。

dp[i]=max(dp[j]+1),i-L<j<i,且s[j]^s[i]\(\le\) x。

dp
就是答案。

但是这样是\(O(n^2)\)。

用 异或字典树 优化为\(O(nlogA)\)。

字典树来维护s[i],总共N个数,最多28位,所以28*N个节点。

要找 j 满足s[j]异或s[i]不超过x的最大的dp[j]+1。我们沿着字典树一位位找,如果x这一位是0,那么s[j]这一位肯定是和s[i]相同,异或起来才不超过x。如果x这一位是1,那么s[j]这一位可以和s[i]相同,直接取这个节点的最大值;也可以是与s[i]不同,然后继续走。

因为长度不超过L,所以每次处理到第i个,要删去第i-L-1个。

因为一个异或值可能出现多次,所以记录出现次数,删去时减少一次出现次数,次数为0时将对应的值设为-1。

代码

#include <cstdio>
#include <algorithm>
#include <cstring>
#define M 268435456
#define N 100005

using namespace std;
int dp
, a
;

struct Trie{
int ch[N*28][2];
int val[N*28];//异或到当前位置不超过x的最多区间数
int cnt[N*28];//出现次数
int size;
void init(){
size=0;
memset(ch,0,sizeof ch);
}
void Insert(int node, int pos, int id, int num){
cnt[node]++;
if(pos<0){
val[node]=dp[id];
return;
}
int bit=(num>>pos)&1;
if(ch[node][bit]==0){
ch[node][bit]=++size;
val[size]=-1;
cnt[size]=0;
}
Insert(ch[node][bit], pos-1, id, num);
val[node]=max(val[node], val[ch[node][bit]]);
}

int Query(int node, int pos, int x, int num){
if(pos<0)
return val[node];
int xbit=(x>>pos)&1, nbit=(num>>pos)&1;
int ans=-1;
if(xbit){
if(ch[node][nbit])
ans = val[ch[node][nbit]];
if(ch[node][!nbit])
ans = max(ans, Query(ch[node][!nbit], pos-1, x, num));
}
else if(ch[node][nbit])
ans = Query(ch[node][nbit], pos-1, x, num);
return ans;
}

void Delete(int node, int pos, int num){
if(!--cnt[node])
val[node]=-1;
if(pos<0)
return;

int bit=(num>>pos)&1;
Delete(ch[node][bit], pos-1, num);
val[node]=val[ch[node][bit]];
if(ch[node][!bit])
val[node]=max(val[node],val[ch[node][!bit]]);
}
}trie;

int main(){
int t;
scanf("%d", &t);
while(t--){
int n,x,l;
scanf("%d%d%d", &n,&x,&l);
int p,q;
scanf("%d%d%d", &a[1],&p,&q);
for(int i=2;i<=n;++i)
a[i]=(a[i-1]*p+q)%M;
for(int i=2;i<=n;++i)a[i]^=a[i-1];

trie.init();trie.Insert(0,27,0,0);

for(int i=1;i<=n;++i){
if(i>l+1&&dp[i-l-1]!=-1)trie.Delete(0, 27, a[i-l-1]);
dp[i]=trie.Query(0, 27, x, a[i]);
if(dp[i]!=-1){
dp[i]++;
trie.Insert(0, 27, i, a[i]);
}
}
printf("%d\n", dp
==-1?0:dp
);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: