zoukankan      html  css  js  c++  java
  • [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\n",Ans[i]);
        }
    }
    
  • 相关阅读:
    小笔记系列——Excel中获取当前日期
    Git 错误:OpenSSL SSL_read: Connection was reset, errno 10054
    cmd_切换文件目录的几种方法
    Jupyter Notebook 常用操作(持续更新中……)
    chrome 浏览器书签保存
    各种开发工具注释的快捷键(持续更新中…)
    Spyder 快捷键(注释、跳转、缩进)
    ISlide插件安装后,PPT无法正常关闭
    [TimLinux] 操作系统实战45讲
    [TimLinux] vnc and go bashrc
  • 原文地址:https://www.cnblogs.com/chasedeath/p/11832142.html
Copyright © 2011-2022 走看看