可以把式子进行拆分,拆分完后,我们关注的是两个整除后的关系以及两个余数之间的关系。
余数只要相减那么答案就是建议,否则答案不变。这是因为两个余数相减,除a[i]是不可能大于1的,但是可以小于0,因为是下取整,所以答案-1.
因此我们可以分三层维护这个答案。
现在的关键是如何快速维护这几个答案。因为a[i]只有1000,这告诉我们余数也不会大于1000,所以我们可以开个二维数组去记录每个a[i]对应的所有b[i]产生的答案
因为询问也不会超过1000,我们发现这个有单调性,可以二分check,复杂度也没问题。

#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e5+10; const ll inf=1e15; typedef long long ll; int n,m; ll sum[2020][2020]; int a[N],b[N]; ll ans=0; bool check(ll x,ll y){ ll res=0; for(int i=1;i<=1000;i++){ res+=sum[i][0]*(x/i); res-=sum[i][x%i+1]; } return res>=y; } ll solve(ll x){ ll l=1,r=inf; while(l<r){ ll mid=l+r>>1; if(check(mid,x)){ r=mid; } else{ l=mid+1; } } return l; } int main(){ ios::sync_with_stdio(false); int t; cin>>t; while(t--){ cin>>n>>m; int i,j; ans=0; for(i=1;i<=1000;i++){ for(j=0;j<=1000;j++) sum[i][j]=0; } for(i=1;i<=n;i++){ cin>>a[i]; } for(i=1;i<=n;i++){ cin>>b[i]; } for(i=1;i<=n;i++){ sum[a[i]][b[i]%a[i]]++; ans+=b[i]/a[i]; } for(i=1;i<=1000;i++){ for(j=i-1;j>=0;j--){ sum[i][j]+=sum[i][j+1]; } } while(m--){ int opt; cin>>opt; ll x,y; if(opt==1){ cin>>x>>y; ans-=b[x]/a[x]; ans+=b[x]/y; for(i=b[x]%a[x];i>=0;i--){ sum[a[x]][i]--; } for(i=b[x]%y;i>=0;i--){ sum[y][i]++; } a[x]=y; } else if(opt==2){ cin>>x>>y; ans-=b[x]/a[x]; ans+=y/a[x]; for(i=b[x]%a[x];i>=0;i--){ sum[a[x]][i]--; } for(i=y%a[x];i>=0;i--){ sum[a[x]][i]++; } b[x]=y; } else{ cin>>x; cout<<solve(x+ans)<<endl; } } } }