HDU 6274 Master of Sequence(思维+树状数组+二分)

题意:

给你n,m。

接下来n个数,给出a[i]。再接下来n个数,给出b[i]。

m次查询,每次查询:

若 id=1 则给出 x y  把a[i]的值修改为y。

若 id=2 则给出 x y  把b[i]的值修改为y。

若 id=3 则给出 k  求,其中

思路:

求S(t)>=k最小的t  显然是二分。

注意S(t)这个式子。我们每次从1~n求的话,复杂度n*m会爆炸。

注意一个诡异的数据范围:1<=ai<=1000

这是解决此题的关键!!!

我们将上式拆开得到t/ai 和 bi/ai   则\sum_{i=1}^{n}\left \lfloor b[i]/a[i] \rfloor\right可以O(n)求出。之后便可以O(1)修改了。

先确定答案为\sum_{i=1}^{n}\left \lfloor t/a[i] \rfloor\right-\sum_{i=1}^{n}\left \lfloor b[i]/a[i] \rfloor\right。然后看向下取整的影响。

而影响答案的只是  ,t%ai 和t%bi。不难发现,对于每个i,当t%ai<t%bi答案需要减1。

因此按a[i]建立1000个树状数组,每个树状数组维护分母为a[i],%a[i]>=x的数字个数即可。

同时预处理每个a[i]出现的次数,这样总时间复杂度就降为O(T*m*1000*log(1000)*log(t))。

10s的时限,足以通过此题。如果还不懂,看代码就很容易理解了:

代码:

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1010;
const int maxm=100010;
int n,m;
int lb(int x){return x&(-x);}
struct BIT
{
    ll c[maxn];
    void init(){memset(c,0,sizeof(c));}
    void add(int i,int v)
    {
        while(i<maxn)
        {
            c[i]+=v;
            i+=lb(i);
        }
    }
    ll query(int i)
    {
        ll sum=0;
        while(i>0)
        {
            sum+=c[i];
            i-=lb(i);
        }
        return sum;
    }
}bit[maxn];
ll sm,b[maxm],a[maxm];
ll cnt[maxn];
bool jud(ll x,ll y)
{
    ll sum=0;
    for(ll i=1;i<=1000;i++)
    {
        sum+=(x/i)*cnt[i]-(cnt[i]-bit[i].query(x%i+1));
        if(sum>=y) return 1;
    }
    return 0;
}
int main()
{
    int T,cas=1;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        memset(cnt,0,sizeof(cnt));
        for(int i=1;i<=1000;i++)
        bit[i].init();
        for(int i=1;i<=n;i++)
        {
           scanf("%lld",&a[i]);
           cnt[a[i]]++;
        }
        sm=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&b[i]);
            sm+=(b[i]/a[i]);
            bit[a[i]].add(b[i]%a[i]+1,1);
        }
        while(m--)
        {
            int id;
            int x,y;
            scanf("%d",&id);
            if(id==1)
            {
                scanf("%d%d",&x,&y);
                cnt[a[x]]--;
                sm=sm-(b[x]/a[x])+(b[x]/(ll)y);
                bit[a[x]].add(b[x]%a[x]+1,-1);

                cnt[y]++;
                bit[y].add(b[x]%y+1,1);
                a[x]=y;
            }
            else if(id==2)
            {
                scanf("%d%d",&x,&y);
                sm=sm-(b[x]/a[x])+((ll)y/a[x]);
                bit[a[x]].add(b[x]%a[x]+1,-1);

                bit[a[x]].add(y%a[x]+1,1);
                b[x]=y;
            }
            else
            {
                ll y;
                scanf("%lld",&y);
                y+=sm;// !!!
                ll l=0,r=1e15;
                while(l<r)
                {
                    ll mid=(l+r)>>1;
                    if(jud(mid,y)) r=mid;
                    else l=mid+1;
                }
                printf("%lld\n",r);
            }
        }
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值