[2017纪中10-31]Calculate 数论

题面
考虑二分答案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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值