T225362 《山茶文具店》

题目链接:《夏摩山谷》 - 洛谷

题目背景

“只要结局完美,过去的种种都算好。” ——《山茶文具店》

题目描述

波波店主收到这样一份代笔
她被要求写下一个仅仅包含 00 到 99 字符串 ss ,但是她可能会对其中的一些数字不是很满意,会经常对某些位置重新修改
而这位顾客会经常来询问店主字符串 ss 下标从 xx 到 yy 形成的十进制数在模 10^9+7109+7 下是多少
店主对修改很适从,但是顾客的奇奇怪怪问题她就没办法解决了,请你帮她解决这个问题

输入格式

第一行一个整数 nn 和一个整数 qq,代表店主的字符串长度以及操作次数(操作包含修改和询问)
接下来一行是一个字符串 ss,代表店主写下的起始字符串
然后是 qq 行操作,opop xx yy

  • op=1op=1:店主修改字符串下标 xx 的位置为 yy
  • op=2op=2:顾客询问字符串 ss 下标从 xx 到 yy 形成的十进制数在模 10^9+7109+7 下是多少

字符串下标从 11 开始
每次修改会将字符串 ss 变成一个新的字符串,即下一次修改是在此基础上进行再次修改

输出格式

对于每次查询,输出一个数,表示字符串 ss 下标从 xx 到 yy 形成的十进制数在模 10^9+7109+7 下的数

输入输出样例

输入 #1复制

5 7
12345
2 1 5
2 2 3
1 2 1
2 1 5
2 2 3
1 5 1
2 1 5

输出 #1复制

12345
23
11345
13
11341

说明/提示

1\le n\le 10^51≤n≤105
1\le q\le 10^51≤q≤105
操作11:1\le x\le n,0\le y\le 91≤x≤n,0≤y≤9
操作22:1\le x\le y\le n1≤x≤y≤n


样例说明

第一次修改之前的两次询问,都是 s=12345s=12345 下的询问
询问11:[12345][12345]
询问22:1[23]451[23]45
第一次修改之后,s=11345s=11345
询问44:[11345][11345]
询问55:1[13]451[13]45
第二次修改之后,s=11341s=11341
询问77:[11341][11341]

思路:在更新的时候,我们观察u的左右子树和u有什么关系

比如u=12,那么左子树的sum就是1,右子树的sum就是2

那么u的sum就是左子树的sum*10的(右子树的长度)次幂+右子树的sum

void pushup(int u){
	ll len=tr[u<<1|1].r -tr[u<<1|1].l +1;
	tr[u].sum =(tr[u<<1].sum *ksm(10,len,mod)%mod+tr[u<<1|1].sum )%mod;
}

单点修改和建树操作还是按板子来就行

对查询操作我们要注意

当我们查询的u的区间被完全包含在我们需要查询的l-r中时

其实得到的值是u的sum*10的(r-u的r)次幂

比如12345

假设我们查的区间是2-5,但是当前结点的区间是2-3,那么我们应该得到的是2300

如果u不被l-r包含的话分别看看与左右子树有没有交集所得值相加就行了(因为左子树的权值我们在完全包含时就处理过了,直接加就行不用乘长度)

ll query(int u,int l,int r){
	if(l<=tr[u].l &&r>=tr[u].r ){
		ll len=r-tr[u].r ;
		return tr[u].sum *ksm(10,len,mod)%mod;
	}else{
		int mid=tr[u].l +tr[u].r >>1;
		ll v1=0,v2=0;
		if(l<=mid)v1=query(u<<1,l,r);
		if(r>mid)v2=query(u<<1|1,l,r);
		return (v1+v2)%mod;
	}
}

注意一下这个细节之后就可以ac啦~

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
const ll mod=1e9+7;
int n,m;
string a;
ll ksm(ll a,ll b,ll p){
	ll res=1%p;
	while(b){
		if(b&1)res=res*a%p;
		a=a*a%p;
		b>>=1;
	}
	return res;
}
struct name{
	int l,r;
	ll sum;
}tr[N*4];
void pushup(int u){
	ll len=tr[u<<1|1].r -tr[u<<1|1].l +1;
	tr[u].sum =(tr[u<<1].sum *ksm(10,len,mod)%mod+tr[u<<1|1].sum )%mod;
//	cout<<"l=="<<tr[u<<1].sum <<" r=="<<tr[u<<1|1].sum <<" sum=="<<tr[u].sum <<endl;
}
void build(int u,int l,int r){
	if(l==r){
		int op=a[l]-'0';
		tr[u]={l,r,op};
	}else{
		tr[u]={l,r,0};
		int mid=l+r>>1;
		build(u<<1,l,mid);
		build(u<<1|1,mid+1,r);
		pushup(u);
	}
}
void modify(int u,int x,int v){
	if(tr[u].l ==x&&tr[u].r ==x){
		tr[u].sum =v;
	}else{
		int mid=tr[u].l +tr[u].r >>1;
		if(x<=mid)modify(u<<1,x,v);
		else modify(u<<1|1,x,v);
		pushup(u);
	}
}
ll query(int u,int l,int r){
	if(l<=tr[u].l &&r>=tr[u].r ){
		ll len=r-tr[u].r ;
		return tr[u].sum *ksm(10,len,mod)%mod;
	}else{
		int mid=tr[u].l +tr[u].r >>1;
		ll v1=0,v2=0;
		if(l<=mid)v1=query(u<<1,l,r);
		if(r>mid)v2=query(u<<1|1,l,r);
		return (v1+v2)%mod;
	}
}
void sove(){
	scanf("%d%d",&n,&m);
	cin>>a;
	a=" "+a;
	build(1,1,n);
	while(m--){
		int op,l,r;
		scanf("%d%d%d",&op,&l,&r);
		if(op==1){
			modify(1,l,r);
		}else{
			cout<<query(1,l,r)<<endl;
		}
	}
}
int main(){
	int t=1;
//	scanf("%d",&t);
	while(t--){
		sove();
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值