魔板

可持久化数据结构是一种保存历史版本的数据结构。
维护指针 \(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;
}