魔板
可持久化数据结构是一种保存历史版本的数据结构。
维护指针 \(p\),\(q\),\(p\)表示当前新加入版本(一开始在新建的根上),\(q\)表示便历上一个版本的节点。
\(p\)从新根开始便历 \(p\) 向 \(q\) 所拥有的点连边,然后在添加上新字符串版本自己新有的节点。同时记录\(cnt[]\),任何时候同位置上\(cnt[p]=cnt[q]+1\)(新加的1个字符);
- 插入
void Insert(int p,int q) {
for(int i=tot;i;i--) {
for(int j=0;j<26;j++)go[p][j]=go[q][j];
Cnt[p]=Cnt[q]+1;
go[p][a[i]]=++c2;
p=go[p][a[i]],q=go[q][a[i]];
}
}
- 查询
int query(int p,int q) {
int len=strlen(s2);
for(int i=len-1;i>=0;i--) {
int k=(s2[i]-'a'+lans)%26;
p=go[p][k],q=go[q][k];
}
return Cnt[q]-Cnt[p];
}
亿些问题
神牛的养成计划
- 题意:n个字符串,m个询问(强制在线),询问给出s1,s2。问前缀为s1,后缀为s2的字符串个数?
- 思路:先用一个普通的trie树维护前缀,并预处理trie树dfs序,按dfs序顺次维护可持久化后缀字典树。然后查询时先查s1,如果查到了且走到了\(u\)节点。然后我们查询\(u\)子树中的节点后缀为s2的个数,即\([In[u],Out[u]]\)内查询个数。
- 代码:
#include<bits/stdc++.h>
using namespace std;
const int N=2e6+5;
const int M=27;
int lans,now[N],tot,Cnt[N],a[N],cnt[N],In[N],rt[N],Out[N],lst[N],Time,lastime,g1[N][M],g2[N][M],c1,c2;
char s1[N],s2[N],s[N];
void Ins1() {
int len=strlen(s);
int u=0;
for(int i=0;i<len;i++) {
int k=s[i]-'a';
if(!g1[u][k]) g1[u][k]=++c1;
u=g1[u][k];
}
cnt[u]++;
}
void Ins2(int p,int q,int w) {
for(int i=tot;i;i--) {
for(int j=0;j<26;j++)g2[p][j]=g2[q][j];
Cnt[p]=Cnt[q]+w;
g2[p][a[i]]=++c2;
p=g2[p][a[i]],q=g2[q][a[i]];
}
}
int query(int p,int q) {
int len=strlen(s2);
for(int i=len-1;i>=0;i--) {
int k=(s2[i]-'a'+lans)%26;
p=g2[p][k],q=g2[q][k];
// printf("%d %d\n",p,q);
}
return Cnt[q]-Cnt[p];
}
void dfs(int u) {
In[u]=++Time;
lst[In[u]]=lastime;
if(cnt[u])rt[Time]=++c2,Ins2(rt[Time],rt[lastime],cnt[u]),lastime=Time;
now[In[u]]=lastime;
for(int i=0;i<26;i++) if(g1[u][i]) a[++tot]=i,dfs(g1[u][i]),tot--;
Out[u]=Time;
}
int _Find() {
int u=0,len=strlen(s1);
for(int i=0;i<len;i++) {
int k=(s1[i]-'a'+lans)%26;
if(g1[u][k]) u=g1[u][k];
else break;
}
// printf("%d %d\n",rt[lst[In[u]]],rt[now[Out[u]]]);
return query(rt[lst[In[u]]],rt[now[Out[u]]]);
}
int main() {
int n,m;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%s",s),Ins1();
dfs(0);
scanf("%d",&m);
for(int j=1;j<=m;j++) {
scanf("%s%s",s1,s2);
int ans=_Find();
printf("%d\n",ans);
lans=ans;
}
return 0;
}
最大异或和
- 题意:P4735
- 思路:处理前缀和,找范围\([l-2,r-1]\)中异或\(x xor sum[n]\)的最大值。
代码不贴了。
最大连续异或和
- 题意:最大连续异或和
- 思路:分块!
\(f[i][j]\): 1.\(r[i]<j\): 从i块更新\([r[i]+1,j]\)异或最大值
\(f[i][j]\): 2.\(l[i]>j\): 从i块更新\([j,l[i]]\)同理
\(val[i]\):表示块i内部两个数异或最大值。
然后查询\([L,R]\)时:
分为开始、结尾暴力,中间的完整块。
开始、结尾是需要技巧的,见代码。
#include<bits/stdc++.h>
using namespace std;
const int N=12005;
int val[N],mx[505][N],b[N],len,n,m,a[N],go[N*32][2],tcnt;
void Insert(int val) {
int u=0;
for(int i=30;i>=0;i--) {
bool x=val&(1<<i);
if(!go[u][x]) go[u][x]=++tcnt;
u=go[u][x];
}
}
int Find(int val) {
int u=0,res=0;
for(int i=30;i>=0;i--) {
bool x=val&(1<<i);
if(go[u][x^1]) u=go[u][x^1],res+=(1<<i);
else u=go[u][x];
}
return res;
}
void Clear() {for(int i=0;i<=tcnt;i++) go[i][0]=go[i][1]=0; tcnt=0;}
int Only(int l,int r,bool cc) {
int res=0;
for(int i=l;i<=r;i++) Insert(a[i]);
for(int i=l;i<=r;i++) res=max(res,Find(a[i]));
if(!cc) Clear();
return res;
}
int solve(int l,int r) {
if(b[l]==b[r]) {return Only(l,r,0);}
int tmp=Only(l,b[l]*len,1);
int res=max(tmp,Only((b[r]-1)*len+1,r,0));
for(int i=b[l]+1;i<b[r];i++) {
res=max(max(mx[i][l],mx[i][r]),max(val[i],res));
}
return res;
}
void init() {
for(int i=1,j;i<=n;i++) {
if(b[i]!=b[i-1]) {
for(j=i;j<=n&&b[j]==b[i];j++) Insert(a[j]);
int r=j-1;
for(int k=i-1;k;k--) mx[b[i]][k]=max(mx[b[i]][k+1],Find(a[k]));
for(int k=i;k<=r;k++) val[b[i]]=max(val[b[i]],Find(a[k]));
for(int k=r+1;k<=n;k++) mx[b[i]][k]=max(mx[b[i]][k-1],Find(a[k]));
Clear();
i=j-1;
}
}
}
int main() {
scanf("%d%d",&n,&m);
n++;
len=(int)sqrt(n);
a[1]=0,b[1]=1;
for(int i=2;i<=n;i++) {
scanf("%d",&a[i]),a[i]^=a[i-1],b[i]=(i-1)/len+1;
}
init();
int lans=0;
for(int i=1;i<=m;i++) {
long long x,y,l,r;
scanf("%lld%lld",&x,&y);
l=min((x+lans)%(n-1)+1,(y+lans)%(n-1)+1);
r=max((x+lans)%(n-1)+1,(y+lans)%(n-1)+1);
// printf("%d %d %d\n",lans,l,r);
lans=solve(l,r+1);
printf("%d\n",lans);
}
return 0;
}
[FJOI2015]火星商店问题
- 题意:P4585
- 思路:线段树分治
线段树上每个节点维护时间段。并存储它所影响的询问。
然后dfs线段树同时下传更新二分后的询问序列(按商店排序)。
然后每次新建可持久化线段树,再询问即可。 - 代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e7+5;
struct query {int l,r,tl,tr,val;}Q[N];
struct node {int p,t,v;}a[N],z[N];
struct seg {int l,r;}T[N];
bool cmp(node u,node v) {return u.p<v.p;}
int g[N],tot,cnt[N<<1],rt[N],c[N],cq,cc,ans[N],go[N<<1][2];
vector<int> q[N];
void Insert(int &e,int q,int val) {
e=++tot; int p=e;
for(int i=19;i>=0;i--) {
int k=val>>i&1;
go[p][k^1]=go[q][k^1],go[p][k]=++tot;
int t=p;
p=go[p][k],q=go[q][k];
cnt[p]=cnt[q]+1;
}
}
int Query(int p,int q,int val) {
int res=0;
for(int i=19;i>=0;i--) {
int k=val>>i&1;
if(cnt[go[p][k^1]]-cnt[go[q][k^1]]) res+=(1<<i),p=go[p][k^1],q=go[q][k^1];
else p=go[p][k],q=go[q][k];
}
return res;
}
void Build(int x,int l,int r) {
T[x]=(seg){l,r};
if(l==r)return;
int mid=(l+r)>>1;
Build(x<<1,l,mid),Build(x<<1|1,mid+1,r);
}
void Update(int x,int k) {
int l=Q[k].tl,r=Q[k].tr;
if(l>r)return;
if(l<=T[x].l&&T[x].r<=r) {q[x].push_back(k);return;}
int mid=(T[x].l+T[x].r)>>1;
if(l<=mid) Update(x<<1,k);
if(r>mid) Update(x<<1|1,k);
}
void Add_nd(int L,int R) { //上一步已经把时间一维处理好了
int num=0;
for(int i=L;i<=R;i++) ++num,Insert(rt[num],rt[num-1],a[i].v),g[num]=a[i].p;
}
void Ask(int x,int tt) {
for(int i=0;i<q[x].size();i++) {
int j=q[x][i];
int l=upper_bound(g+1,g+1+tt,Q[j].l-1)-g-1;
int r=upper_bound(g+1,g+1+tt,Q[j].r)-g-1;
ans[j]=max(ans[j],Query(rt[r],rt[l],Q[j].val));
}
}
void dfs(int x,int L,int R) { //[L,R]为修改操作范围
if(L>R)return;
//更新询问
Add_nd(L,R);
Ask(x,R-L+1);
if(T[x].l==T[x].r)return;
//更新修改序列顺序
int num=L-1,mid2,mid=(T[x].l+T[x].r)>>1;
for(int i=L;i<=R;i++) z[i]=a[i];
for(int i=L;i<=R;i++) if(z[i].t<=mid) a[++num]=z[i];
mid2=num;
for(int i=L;i<=R;i++) if(z[i].t>mid) a[++num]=z[i];
// printf("%d %d\n",num,R);
dfs(x<<1,L,mid2),dfs(x<<1|1,mid2+1,R);
}
int main() {
int n,m,T=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&c[i]),Insert(rt[i],rt[i-1],c[i]);
for(int i=1;i<=m;i++) {
int opt,l,r,x,d;
scanf("%d",&opt);
if(!opt) {
T++;
scanf("%d%d",&x,&d);
a[++cc]=(node){x,T,d};
}
else {
scanf("%d%d%d%d",&l,&r,&x,&d);
Q[++cq]=(query){l,r,max(T-d+1,1),T,x};
ans[cq]=Query(rt[r],rt[l-1],x);
// printf("%d\n",ans[cq]);
}
}
sort(a+1,a+1+cc,cmp);
Build(1,1,T);
for(int i=1;i<=cq;i++) Update(1,i);
dfs(1,1,T);
for(int i=1;i<=cq;i++) printf("%d\n",ans[i]);
return 0;
}