题解 Luogu P4891 序列

题意

给定两个长度为 n 的非负整数序列 A,B,并定义数列 CCi=maxj=1iAi

q 次操作,每次对 AB 的某个位置进行修改,然后询问 i=1nmin{Bi,Ci}mod109+7保证修改之后不小于原数。

1n,q105,任意时刻 0Ai,Bi109

题解

考虑一个修改的影响。B 的影响是单点的,但修改 Ai 的影响是一段以 i 开始的区间。

注意到这个区间修改比较阴间,因为维护的信息非常怪,不太能我不会线段树维护。考虑分块,维护块内的答案。对于修改 Bi 直接暴力重构块就行了。接下来重点讨论怎么维护修改了 Ai 之后的 C

我们发现 Ai 被修改了之后会产生一个 [i,n]C 序列区间取 max 操作。套路,散块暴力重构,整块直接打标记。考虑 tag=k 的时候的答案。

分类讨论。

  • BjCj

    此时 j 的贡献一定是 Bi

  • CjtagBj

    此时贡献是 tag

  • CjBjtag

    此时贡献是 Bj

  • tagCjBj

    贡献是 Cj

考虑把贡献不为 tag 的部分记下来,记作 mul。贡献为 tag 的部分直接记录指数 c。显然第一种情况的 Bj 要乘到 mul 里面。然后维护后面三种情况。注意到 tag 单调不减,于是一个 j 的贡献一定是先在第四种情况,然后第二种,最后第三种。

把所有 BjCjCjBj 放一起排序,tag 从前往后扫所有比它小的数,扫到一个 Cj 就说明是第二种情况,mul 除掉 Cj,然后贡献多了一个 tag。扫到一个 Bj 说明是第三种情况了,贡献少一个 tagmul 乘上 Bj

块内答案就是 mul×tagc,于是查询是根号的。

复杂度还有个快速幂的 log 和排序的 log,于是是 O(nnlogmaxW),其中 W 是值域,默认 n,q 同阶。

另外可能会乘 0 和除 0,需要精细实现一下。具体见代码。

# include <bits/stdc++.h>
const int N=100010,INF=0x3f3f3f3f,BLEN=410,MOD=1e9+7;
typedef long long ll;
int bsiz,blo[N];
int n,m;
int tc[N],tb[N],ta[N];
inline int qpow(ll d,ll p){ 
	ll ans=1;
	while(p){
		if(p&1) ans=ans*d%MOD;
		p>>=1,d=d*d%MOD;
	}
	return ans;
}
struct Prod{ // 支持乘 0 和除 0
	int z,cur; // z 是现在乘了的 0 的个数, cur 是除了 0 以外部分的乘积
	inline void setv(int x){ 
		z=0,cur=x;
		return;
	}
	inline void mul(int x){
		x?(cur=1ll*cur*x%MOD):(++cur);
		return;
	}
	inline void div(int x){
		x?(cur=1ll*cur*qpow(x,MOD-2)%MOD):(--cur);
		return;
	}
	inline int getv(void){
		return (!z)*cur; 
	}
};
struct Block{
	int len,c[BLEN],b[BLEN],tag,pos,lbc,L,R;
	Prod ans;
	std::vector <std::pair <int,int> > S;
	inline void init(int cl,int cr){ // 初始化
		len=cr-cl+1,L=cl,R=cr;
		for(int i=0;i<len;++i) c[i+1]=tc[cl+i],b[i+1]=tb[cl+i];
		return;
	}
	inline void remake(void){ // 暴力重构
		for(int i=1;i<=len;++i) c[i]=std::max(c[i],tag); // 下放标记
		ans.setv(1);
		for(int i=1;i<=len;++i) ans.mul(std::min(c[i],b[i]));
		S.clear();
		for(int i=1;i<=len;++i) if(b[i]>c[i]) S.push_back(std::make_pair(b[i],1)),S.push_back(std::make_pair(c[i],0));
		std::sort(S.begin(),S.end()),pos=lbc=0; // 归零 c 和 pos, 重新排序
	}
	inline void modifytag(int x){
		if(x<=tag) return;
		tag=x;
		while(pos<(int)S.size()&&S[pos].first<=tag){
			if(S[pos].second) --lbc,ans.mul(S[pos].first);
			else ++lbc,ans.div(S[pos].first);
			++pos;
		}
		return;
	}
	inline int query(void){
		return 1ll*ans.getv()*qpow(tag,lbc)%MOD;
	}
}B[BLEN];

inline int read(void){
	int res,f=1;
	char c;
	while((c=getchar())<'0'||c>'9')
		if(c=='-')f=-1;
	res=c-48;
	while((c=getchar())>='0'&&c<='9')
		res=res*10+c-48;
	return res*f;
}
void print(int x){
	if(x>9) print(x/10);
	putchar(x%10+'0');
	return; 
}
signed main(void){
	n=read(),m=read(),bsiz=floor(sqrt(n)+0.5);
	for(int i=1;i<=n;++i) ta[i]=read(),blo[i]=(i-1)/bsiz+1;
	for(int i=1;i<=n;++i) tb[i]=read(),tc[i]=std::max(tc[i-1],ta[i]);
	for(int i=1;i<=blo[n];++i){
		B[i].init((i-1)*bsiz+1,std::min(i*bsiz,n)),B[i].remake();
	}
	while(m--){
		int opt=read(),pos=read(),v=read();
		if(opt){
			int idx=blo[pos];
			B[idx].b[pos-B[idx].L+1]=v;
			B[idx].remake(); // 重构块
		}else{
			int idx=blo[pos];
			for(int i=pos;i<=B[idx].R;++i) B[idx].c[i-B[idx].L+1]=std::max(B[idx].c[i-B[idx].L+1],v); // 散块暴力修改
			B[idx].remake(); // 重构
			for(int i=idx+1;i<=blo[n];++i) B[i].modifytag(v);
		}
		int ans=1;
		for(int i=1;i<=blo[n];++i) ans=1ll*ans*B[i].query()%MOD;
		print(ans),puts("");
	}
	
	return 0;
}

作者:Meatherm

出处:https://www.cnblogs.com/Meatherm/p/16479589.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   Meatherm  阅读(39)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
more_horiz
keyboard_arrow_up light_mode palette
选择主题
点击右上角即可分享
微信分享提示