[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]); } }