题解 Luogu P4891 序列
题意
给定两个长度为 的非负整数序列 ,并定义数列 ,。
有 次操作,每次对 和 的某个位置进行修改,然后询问 。保证修改之后不小于原数。
,任意时刻
题解
考虑一个修改的影响。 的影响是单点的,但修改 的影响是一段以 开始的区间。
注意到这个区间修改比较阴间,因为维护的信息非常怪,不太能我不会线段树维护。考虑分块,维护块内的答案。对于修改 直接暴力重构块就行了。接下来重点讨论怎么维护修改了 之后的 。
我们发现 被修改了之后会产生一个 的 序列区间取 操作。套路,散块暴力重构,整块直接打标记。考虑 的时候的答案。
分类讨论。
-
此时 的贡献一定是 。
-
此时贡献是 。
-
此时贡献是 。
-
贡献是 。
考虑把贡献不为 的部分记下来,记作 。贡献为 的部分直接记录指数 。显然第一种情况的 要乘到 里面。然后维护后面三种情况。注意到 单调不减,于是一个 的贡献一定是先在第四种情况,然后第二种,最后第三种。
把所有 的 和 放一起排序, 从前往后扫所有比它小的数,扫到一个 就说明是第二种情况, 除掉 ,然后贡献多了一个 。扫到一个 说明是第三种情况了,贡献少一个 , 乘上 。
块内答案就是 ,于是查询是根号的。
复杂度还有个快速幂的 和排序的 ,于是是 ,其中 是值域,默认 同阶。
另外可能会乘 和除 ,需要精细实现一下。具体见代码。
# 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 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现