【题解】HDU5845 Best Division (tri树)

题意:给定你一个序列(三个参数来根),然后请你划分子段。在每段子段长度小于等于\(L\)且子段的异或和\(\le x\)的情况下最大化分出子段的个数

区间/子段/序列这种东西一大性质就是右端点之后与前面无关。

\(dp(i)\)表示上一个右端点是\(i\)且前面分出来的子段都满足条件的最多子段个数。

直接转移\(O(n^2)\)考虑优化这个东西,子段转前缀和是应该记起来的套路,现在问题就是变成查询两个前缀和的异或值是否满足条件。

先不考虑\(L\)的限制

处理两者异或的数据结构=trie树,于是可以建立一颗01trie,trie的叶子节点可以表示一个值,建立trie输入的字符串就是他对应位置的前缀和。

考虑假如已经有了一个trie树了,在trie树上查询的实质是生成一个新的数。我们可以在生成这个数的时候根据该当前生成位上的新生成的那个数能否为\(1\),以及\(x\)是否为\(1\)讨论一下一个节点子树最大值。

查询子树最大值可以直接线段树+dfs序,但这是数据结构学傻的表现。可以发现trie树树高是\(\log n\)的,直接在插入的时候暴力跳父亲更新父亲的值即可。

考虑\(L\)的限制,这个限制就相当于要让这个trie树支持删除操作,可以每个节点套一个multiset,但这是stl用傻了的表现。可以发现dp数组的值是不降的(这里其实存疑),所以可以直接删。

复杂度\(O(n\log n)\)

//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>

using namespace std;  typedef long long ll;
inline int qr(){
      register int ret=0,f=0;
      register char c=getchar();
      while(!isdigit(c))f|=c==45,c=getchar();
      while(isdigit(c)) ret=ret*10+c-48,c=getchar();
      return f?-ret:ret;
}

const int maxn=1e5+5;
const int inf=0x3f3f3f3f;
const int mod=(1<<28)-1;

struct E{
      int son[2],Max,cnt;
      inline void clear(){memset(son,0,sizeof son);Max=0;cnt=0;}
      inline int&operator[](int x){return son[x];}
      inline void upd(const int&val,const int&tag){
        if(tag==1){
          if(Max<val) Max=val,cnt=1;
          else if(Max==val) ++cnt;
        }
        else {
          if(Max==val) --cnt;
          if(cnt==0) Max=0;
        }
      }
}e[maxn*28];
int sum[maxn],dp[maxn],cnt,n,x,L,p,q;

inline void insert(const int&x,const int&val){
      int now=0;
      for(int t=27;~t;--t){
        int g=x>>t&1;
        if(!e[now][g]) e[now][g]=++cnt,e[cnt].clear();
        now=e[now][g];
        e[now].upd(val,1);
      }
}

inline void Delete(const int&x,const int&val){
      int now=0;
      for(int t=27;~t;--t){
        int g=x>>t&1;
        now=e[now][g];
        e[now].upd(val,-1);
      }
}

inline int que(const int&s,const int&x){
      int ret=-1,now=0;
      for(int t=27;~t;--t){
        int g=s>>t&1;
        if(x>>t&1){
          if(e[now][g]) ret=max(ret,e[e[now][g]].Max);
          if(e[now][g^1]) now=e[now][g^1];
          else return ret;
        }
        else{
          if(e[now][g]) now=e[now][g];
          else return ret;
        }
      }
      ret=max(ret,e[now].Max);
      return ret;
}

int main(){
#ifndef ONLINE_JUDGE
      freopen("in.in","r",stdin);
      //freopen("out.out","w",stdout);
#endif
      int T=qr();
      while(T--){
        n=qr(); x=qr(); L=qr();
        sum[1]=qr(); p=qr(); q=qr();
        memset(e,0,sizeof e);
        e[cnt=0].clear();
        for(int t=2;t<=n;++t) sum[t]=(1ll*sum[t-1]*p+q)&mod;
        for(int t=2;t<=n;++t) sum[t]^=sum[t-1];
        memset(dp,-1,sizeof dp);
        insert(0,0); dp[0]=0;
        for(int t=1,l=t-L-1,k;t<=n;++t,++l){
          if(l>=0) if(dp[l]>=0) Delete(sum[l],dp[l]);
          k=que(sum[t],x);
          if(k>=0) dp[t]=k+1,insert(sum[t],dp[t]);
        }
        printf("%d\n",dp[n]==-1?0:dp[n]);
      }
      return 0;
}

01-12 06:00