题面
考虑二分答案T。问题转化成如何高效计算S(T)。
化式子:
分开来除,加上他们的余数和的贡献。
发现Ai最大1000,考虑把(Ai,Bi)按照Ai分类,每一类维护cnt,sum,rst[0~Ai-1],分别表示数对的个数,(-Bi)/Ai的和,(-Bi)%Ai取值的前缀和,那么对于每一个Ai都可以O(1)计算,对于每个查询O(1000*log10^12)。
修改直接暴力修改即可,对于每个修改O(1000)。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=100010;
int n,m,a[maxn],b[maxn];
ll sum[1010],cnt[1010],rst[1010][1010];
int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar(); }
while(ch>='0'&&ch<='9') {x=x*10+ch-'0'; ch=getchar(); }
return x*f;
}
inline ll div(ll a,int b)
{
ll t=a/b;
if(a>=0||b*t==a) return t;
return t-1;
}
inline ll mod(ll a,int b)
{
ll t=a%b;
if(t>=0) return t;
else return t+b;
}
ll cal(ll x)
{
ll re=0;
for(int i=1;i<=1000;i++)
if(cnt[i]>0)
{
ll t=div(x,i);
re+=sum[i]+t*cnt[i]+rst[i][i-1]-rst[i][i-(x-t*i)-1];
}
return re;
}
int main()
{
int ca=read();
while(ca--)
{
n=read();m=read();
for(int i=1;i<=n;i++)
a[i]=read();
for(int i=1;i<=n;i++)
b[i]=-read();
memset(cnt,0,sizeof(cnt));
memset(sum,0,sizeof(sum));
memset(rst,0,sizeof(rst));
for(int i=1;i<=n;i++)
{
cnt[a[i]]++;
sum[a[i]]+=div(b[i],a[i]);
rst[a[i]][mod(b[i],a[i])]++;
}
for(int i=1;i<=1000;i++)
for(int j=1;j<i;j++)
rst[i][j]+=rst[i][j-1];
while(m--)
{
int opt=read(),x=read();
if(opt==1)
{
sum[a[x]]-=div(b[x],a[x]);cnt[a[x]]--;
for(int i=mod(b[x],a[x]);i<a[x];i++)
rst[a[x]][i]--;
a[x]=read();
sum[a[x]]+=div(b[x],a[x]);cnt[a[x]]++;
for(int i=mod(b[x],a[x]);i<a[x];i++)
rst[a[x]][i]++;
}
if(opt==2)
{
sum[a[x]]-=div(b[x],a[x]);
for(int i=mod(b[x],a[x]);i<a[x];i++)
rst[a[x]][i]--;
b[x]=-read();
sum[a[x]]+=div(b[x],a[x]);
for(int i=mod(b[x],a[x]);i<a[x];i++)
rst[a[x]][i]++;
}
if(opt==3)
{
ll l,r;
if(n>=990) l=-1e9,r=1e9;
else l=-2e12,r=2e12;
while(l<r)
{
ll mid=(l+r)>>1;
if(cal(mid)>=x) r=mid;
else l=mid+1;
}
printf("%lld\n",l);
}
}
}
return 0;
}