HDU6274&&2017CCPC杭州K Master of Sequence 【二分+预处理】

传送门
这里写图片描述

题意描述:
nabm 给 长 度 为 n 的 数 组 a 和 b 。 然 后 有 m 次操作,1)把 axy a x 的 值 变 为 y ;2)把 bx b x 的值变为y;
3)给定 k k ,求min(t|k<=S(t)) 其中 S(t)=ni=1tbiai S ( t ) = ∑ i = 1 n ⌊ t − b i a i ⌋

分析:通过分析题意,题目的关键在于快速处理操作三。操作三查询有序区间的最小值,一般采用二分法解决,然后就是在一定时间范围内对任意 t t ,快速求出S(t) 。对于 tbiai t − b i a i 可以化简为 k1ai+c1k2aic2ai k 1 ∗ a i + c 1 − k 2 ∗ a i − c 2 a i
( t=k1ai+c1,bi=k2ai+c2 设 t = k 1 ∗ a i + c 1 , b i = k 2 ∗ a i + c 2 ) 。由于 b b 是在可控范围内变化,可以预处理出所有的i=1nk2i
因为是向下取整,就需要比较所有的 c1c2 c 1 和 c 2 ,由于 a a 数组范围为只有1000,我们可以通过预处理求出在1000范围 所有的 aic2,对于每个t它的 c1 c 1 是固定的。故而我们可以在 O(1000) O ( 1000 ) 时间求出 S(t) S ( t ) 。具体操作见代码。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=100100;
int a[N],b[N],num[1010][1010],n; //num[x][y]表示对于所有a[i]==x时的b[i]%a[i]余数大于等于y的数目
bool check(LL x,LL s) 
{
    LL ret=0;
    for(int i=1; i<=1000; i++)
    {
        ret+=x/i*num[i][0]; //num[i][0]为a数组中等于i的数的个数
        ret-=num[i][x%i+1];  //减掉所有 c1<c2 的情况
    }
    if(s<=ret)
        return true;
    return false;
}
LL solve(LL x)
{
    LL l=1,r=1e13,mid;
    while(l<r)
    {
        mid=(l+r)>>1;
        if(check(mid,x))
            r=mid;
        else
            l=mid+1;
    }
    return l;
}
int main()
{
    int TA,T,x,y,z;
    LL ret;
    scanf("%d",&TA);
    while(TA--)
    {
        scanf("%d%d",&n,&T);
        ret=0;
        for(int i=1; i<=1000; i++)
            for(int j=0; j<i; j++)
                num[i][j]=0;
        for(int i=1; i<=n; i++)
            scanf("%d",&a[i]);
        for(int i=1; i<=n; i++)
            scanf("%d",&b[i]),ret+=(b[i]/a[i]),num[a[i]][b[i]%a[i]]++;
        for(int i=1; i<=1000; i++)
            for(int j=i-1; j>=0; j--)
                num[i][j]+=num[i][j+1];
        while(T--)
        {
            scanf("%d%d",&z,&x);
            if(z==1)
            {
                scanf("%d",&y);
                for(int i=b[x]%a[x]; i>=0; i--)
                    num[a[x]][i]--;
                for(int i=b[x]%y; i>=0; i--)
                    num[y][i]++;
                ret-=(b[x]/a[x]);
                ret+=(b[x]/y);
                a[x]=y;
            }
            else if(z==2)
            {
                scanf("%d",&y);
                for(int i=b[x]%a[x]; i>=0; i--)
                    num[a[x]][i]--;
                for(int i=y%a[x]; i>=0; i--)
                    num[a[x]][i]++;
                ret-=(b[x]/a[x]);
                ret+=(y/a[x]);
                b[x]=y;
            }
            else
            {
                printf("%lld\n",solve(ret+x));
            }
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值