专栏文章

【题录】数据结构杂题

个人记录参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@min73e3h
此快照首次捕获于
2025/12/01 21:38
3 个月前
此快照最后确认于
2025/12/01 21:38
3 个月前
查看原文

Lucky Numbers

题面


思路

考虑直接用线段树维护,每个节点维护 33 个大标记,每个大标记维护 44 个小标记,具体如下:
  • 小标记:ansansbegin3begin3end1end1begin3end1begin3end1
    其中 ansans 表示总合法数量,begin3begin3 表示开头为 33 的合法数量,end1end1 表示结尾为 11 的合法数量,begin3end1begin3end1 表示开头为 33 且结尾为 11 的合法数量。
  • 大标记:lesslessequalequalgreatergreater
    其中 lessless 表示小于原序列的合法数量,equalequal 表示等于原序列的合法数量,greatergreater 表示大于原序列的合法数量。
合并:
  • 小标记:
    • ansl.ans×r.ansl.end1×r.begin3ans\leftarrow l.ans\times r.ans-l.end1\times r.begin3
    • begin3l.begin3×r.ansl.begin3end1×r.begin3begin3\leftarrow l.begin3\times r.ans-l.begin3end1\times r.begin3
    • end1l.ans×r.end1l.end1×r.begin3end1end1\leftarrow l.ans\times r.end1-l.end1\times r.begin3end1
    • begin3end1l.begin3×r.end1l.begin3end1×r.begin3end1begin3end1\leftarrow l.begin3\times r.end1-l.begin3end1\times r.begin3end1
  • 大标记:
    • lessl.equal×r.less+l.less×(r.less+r.equal+r.greater)less\leftarrow l.equal\times r.less+l.less\times(r.less+r.equal+r.greater)
    • equall.equal×r.equalequal\leftarrow l.equal\times r.equal
    • greaterl.equal×r.greater+l.greater×(r.less+r.equal+r.greater)greater\leftarrow l.equal\times r.greater+l.greater\times(r.less+r.equal+r.greater)
    其中,×\times 是以小标记的合并方式将两个大标记的小标记合并,而 ++ 是直接将两个大标记的小标记相加。
叶子节点分类讨论后,按如上方式维护线段树即可。

代码

CPP
#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
struct tag{
	long long ans,b3,e1,b3e1;
};
tag operator + (tag x,tag y){
	tag z;
	z.ans=(x.ans+y.ans)%mod;
	z.b3=(x.b3+y.b3)%mod;
	z.e1=(x.e1+y.e1)%mod;
	z.b3e1=(x.b3e1+y.b3e1)%mod;
	return z;
}
//两个大标记相加为将其小标记相加 
tag operator * (tag x,tag y){
	tag z;
	z.ans=(x.ans*y.ans-x.e1*y.b3%mod+mod)%mod;
	z.b3=(x.b3*y.ans-x.b3e1*y.b3%mod+mod)%mod;
	z.e1=(x.ans*y.e1-x.e1*y.b3e1%mod+mod)%mod;
	z.b3e1=(x.b3*y.e1-x.b3e1*y.b3e1%mod+mod)%mod;
	return z;
}
//两个大标记相乘为将其小标记合并 
struct node{
	tag l,e,g;
};
inline node merge(node x,node y){
	node z;
	z.l=x.e*y.l+x.l*(y.l+y.e+y.g);
	z.e=x.e*y.e;
	z.g=x.e*y.g+x.g*(y.l+y.e+y.g);
	return z;
}
//大标记的合并 
int n,q,a[100010];
node tree[400010];
inline void init(int x,int v){
	tree[x].l.ans=v;
	tree[x].e.ans=1;
	tree[x].g.ans=9-v;
	if(v<1)
	{
		tree[x].l.e1=0;
		tree[x].e.e1=0;
		tree[x].g.e1=1;
	}
	if(v==1)
	{
		tree[x].l.e1=0;
		tree[x].e.e1=1;
		tree[x].g.e1=0;
	}
	if(v>1)
	{
		tree[x].l.e1=1;
		tree[x].e.e1=0;
		tree[x].g.e1=0;
	}
	if(v<3)
	{
		tree[x].l.b3=0;
		tree[x].e.b3=0;
		tree[x].g.b3=1;
	}
	if(v==3)
	{
		tree[x].l.b3=0;
		tree[x].e.b3=1;
		tree[x].g.b3=0;
	}
	if(v>3)
	{
		tree[x].l.b3=1;
		tree[x].e.b3=0;
		tree[x].g.b3=0;
	}
	tree[x].l.b3e1=0;
	tree[x].e.b3e1=0;
	tree[x].g.b3e1=0;
}
//线段树叶子节点的分类讨论 
inline void pushup(int x){
	tree[x]=merge(tree[x*2],tree[x*2+1]);
}
void build(int l,int r,int x){
	if(l==r)
	{
		init(x,a[l]);
		return;
	}
	int mid=(l+r)>>1;
	build(l,mid,x*2);
	build(mid+1,r,x*2+1);
	pushup(x);
}
void change(int l,int r,int x,int k,int v){
	if(l==r)
	{
		init(x,v);
		return;
	}
	int mid=(l+r)>>1;
	if(k<=mid)change(l,mid,x*2,k,v);
	else change(mid+1,r,x*2+1,k,v);
	pushup(x);
}
node ask(int l,int r,int x,int ll,int rr){
	if(l>=ll&&r<=rr)return tree[x];
	int mid=(l+r)>>1;
	if(rr<=mid)return ask(l,mid,x*2,ll,rr);
	if(ll>mid)return ask(mid+1,r,x*2+1,ll,rr);
	return merge(ask(l,mid,x*2,ll,rr),ask(mid+1,r,x*2+1,ll,rr));
}
//线段树维护标记 
int main(){
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++)
	{
		char c;
		cin>>c;
		a[i]=c-'0';
	}
	build(1,n,1);
	printf("%lld\n",(tree[1].l.ans+tree[1].e.ans)%mod);
	while(q--)
	{
		int op;
		scanf("%d",&op);
		if(op==1)
		{
			int l,r;
			scanf("%d%d",&l,&r);
			node s=ask(1,n,1,l,r);
			printf("%lld\n",(s.l.ans+s.e.ans)%mod);
		}
		else
		{
			int k,v;
			scanf("%d%d",&k,&v);
			change(1,n,1,k,v);
		}
	}
	return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...