[HDU4867]Xor (线段树分治+类数位dp)

Posted chasedeath

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[HDU4867]Xor (线段树分治+类数位dp)相关的知识,希望对你有一定的参考价值。

[HDU4867]Xor (线段树分治+类数位dp)

提供一种((m+n) log a log m)带有常数约(frac{1}{log n})的算法

处理询问,将后来加入的数算进序列中,则每个数(a_i)都有一段出现的区间([L,R])

离线询问后,我们考虑用线段树分治将这些数加入到询问区间上

由于最多只有5000个修改操作,事实上这些数在线段树上覆盖的区间最多只有(10000logm)个,并且有着极其不满的常数(因为每个位置上的数都由多段区间组合而来,总长为(m),或者你可以觉得我在放屁)

如果直接处理每个数的贡献,那么这个(dp)(a*a)转移的

然而事实上我们存在一种(a*loga)的转移方法

对于一个数(x),如果我们取(y leq x)时,最高位为(0),则后面的位均可以随便取

换句话说,对于每一个前(k)位相同的集合,它们都能够转移到它们之间的任何一个,可以直接累和

同样的,考虑在第(k)位出现一个(x)在该位为(1)(y)(0),都具有类似的转移性质

最后写出来跟数位(dp)一个样子。。。

(真不行你可以试试某变换,但是我不会!)

这样的情况个数即这个数(1)位的个数,这样的个数期望情况下可以看做常数。。。

所以我们得到了一个期望优秀的算法,实际运行时间也非常优秀

#include<cstdio>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)


char IO;
int rd(){
    int s=0,f=0;
    while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    do s=(s<<1)+(s<<3)+(IO^'0');
    while(isdigit(IO=getchar()));
    return f?-s:s;
}

const int N=1e5+10,P=1e9+7;

int n,m,E[N],Now[N],L[N],R[N],A=1023;
int dp[1024],tmp[1024],tmp2[1024];
int a[20],l,Ans[N],Qx[N],sq[N];
char opt[N];

#define Mod(x) ((x>=P)&&(x-=P))

void Solve(int p,int Up,int lim){
    if(p<0) {
        rep(S,0,A) tmp[S]+=dp[S^Up],Mod(tmp[S]);
        return;
    }
    if(!lim) {
        reg int t,Down=(1<<(p+1))-1;
        rep(S,0,A) tmp2[S]=0;
        rep(S,0,A) tmp2[t=Up^S^(S&Down)]+=dp[S],Mod(tmp2[t]); // 前几位相同的累和
        rep(S,0,A) tmp[S]+=tmp2[S^(S&Down)],Mod(tmp[S]);
        return;
    }
    rep(i,0,a[p]) Solve(p-1,Up|(i<<p),i==a[p]);
}

void Add(int x){
    if(!x) return;
    l=-1;
    while(x) a[++l]=(x&1),x>>=1;
    Solve(l,0,1);
    rep(S,0,A) dp[S]=tmp[S],tmp[S]=0;
}

vector <int> G[N];
void AddQue(int p,int l,int r,int ql,int qr,int x){
    if(l==ql&&r==qr) {
        G[p].push_back(x);
        return;
    }
    int mid=(l+r)>>1;
    if(qr<=mid) AddQue(p<<1,l,mid,ql,qr,x);
    else if(ql>mid) AddQue(p<<1|1,mid+1,r,ql,qr,x);
    else AddQue(p<<1,l,mid,ql,mid,x),AddQue(p<<1|1,mid+1,r,mid+1,qr,x);
}

int tmp3[20][1024];
void AnsQue(int p,int l,int r,int dep){
    rep(S,0,A) tmp3[dep][S]=dp[S];
    rep(i,0,G[p].size()-1) Add(G[p][i]);
    if(l==r) {
        Ans[l]=dp[Qx[l]];
        return;
    }
    int mid=(l+r)>>1;
    AnsQue(p<<1,l,mid,dep+1);
    AnsQue(p<<1|1,mid+1,r,dep+1);
    rep(S,0,A) dp[S]=tmp3[dep][S];
}

int main(){
    rep(kase,1,rd()) {
        memset(dp,0,sizeof dp),dp[0]=1;
        n=rd(),m=rd();
        rep(i,1,n) Now[i]=i,E[i]=rd(),L[i]=1,R[i]=m;
        rep(i,1,m*4) G[i].clear();
        rep(i,1,m) {
            while(!isalpha(opt[i]=getchar()));
            if(opt[i]=='C') {
                int x=rd()+1,y=rd();
                R[Now[x]]=i-1;
                Now[x]=++n;
                E[n]=y;
                L[n]=i;
                R[n]=m;
            } else Qx[i]=rd();
            sq[i]=sq[i-1]+(opt[i]=='Q');
        }
        rep(i,1,n) AddQue(1,1,m,L[i],R[i],E[i]);
        AnsQue(1,1,m,0);
        rep(i,1,m) if(opt[i]=='Q') printf("%d
",Ans[i]);
    }
}

以上是关于[HDU4867]Xor (线段树分治+类数位dp)的主要内容,如果未能解决你的问题,请参考以下文章

hdu 6889 数位dp Xor

七月份前计划清单

hdu 4366 Successor - CDQ分治 - 线段树 - 树分块

[基本操作]线段树分治和动态dp

Xor HDU - 6899

Xor HDU - 6899