HDU 6274(思维+数组/树状数组)

题目大意:

 

• 1 x y: 将ax改成y
• 2 x y: 将bx改成y
• 3 k: ask min{t|k≤S(t)}

 

题目思路:设t=k1*ai+c1,bi=k2*ai+c2,原式变为(k1*ai+c1-(k2*ai+c2))/a[i]的向下取整。c1>=c2时,由于其范围都在0~ai-1内,所以c1-c2还是在0~ai-1内,对答案不产生影响,若c1<c2,那么就需要原答案-1,那么就开一个num[x][y]数组,表示当a为x时,b%a>=y的情况数。接着二分t,由于可以移项,所以等价于check函数得到的值大于等于查询的值+k2的和,x/i得到k1,一共有num[i][0]个a[i]的数,那就说明有k1求出c2>=c1,c1用x%a得出。由于查询和修改涉及0,树状数组修改的点的位置需要+1。对于修改,每次都去掉之前的影响,换成新的影响即可。

 

以下是代码:

数组版本:

#include<bits/stdc++.h>
 
using namespace std;
#define inf 0x3f3f3f3f
#define rep(i,a,b) for(ll i=a;i<=b;i++)
#define per(i,a,b) for(ll i=a;i>=b;i--)
#define ll long long
#define PI  acos(-1)
const ll MAXN = 1e5+5;
ll a[MAXN],b[MAXN];
ll num[1005][1005];
ll temp;
bool check(ll x){
    ll rst=0;
    rep(i,1,1000){
        rst+=x/i*num[i][0];
        rst-=num[i][x%i+1];
    }
    if(rst>=temp)return true;
    return false;
}
int main()
{
    ll t,n,m,z,x,y;
    scanf("%lld",&t);
    while(t--){
        memset(num,0,sizeof(num));
        scanf("%lld%lld",&n,&m);
        ll rst=0;
        rep(i,1,n)scanf("%lld",&a[i]);
        rep(i,1,n)scanf("%lld",&b[i]),rst+=b[i]/a[i],num[a[i]][b[i]%a[i]]++;
        rep(i,1,1000){
            per(j,i-1,0){
                num[i][j]+=num[i][j+1];
            }
        }
        while(m--){
            scanf("%lld",&z);
            if(z==1){
                scanf("%lld%lld",&x,&y);
                rst-=b[x]/a[x];
                per(i,b[x]%a[x],0)num[a[x]][i]--;
                rep(i,0,b[x]%y)num[y][i]++;
                a[x]=y;
                rst+=b[x]/y;
            }
            else if(z==2){
                scanf("%lld%lld",&x,&y);
                rst-=b[x]/a[x];
                per(i,b[x]%a[x],0)num[a[x]][i]--;
                rep(i,0,y%a[x])num[a[x]][i]++;
                b[x]=y;
                rst+=y/a[x];
            }
            else{
                scanf("%lld",&x);
                temp=rst+x;
                ll l=1,r=1e13,ans=-1;
                while(l<=r){
                    ll mid=(l+r)>>1;
                    if(check(mid)){
                        r=mid-1;
                        ans=mid;
                    }
                    else{
                        l=mid+1;
                    }
                }
                printf("%lld\n",ans);
            }
        }
    }
    return 0;
}

树状数组版本:

include<bits/stdc++.h>
 
using namespace std;
#define inf 0x3f3f3f3f
#define rep(i,a,b) for(ll i=a;i<=b;i++)
#define per(i,a,b) for(ll i=a;i>=b;i--)
#define ll long long
#define PI  acos(-1)
const ll MAXN = 1e5+5;
ll a[MAXN],b[MAXN];
ll num[1005][1005];
ll temp;
void add(ll p,ll x,ll y){x++;
    for(;x;x-=x&-x)num[p][x]+=y;
}
ll query(ll p,ll x){x++;
    ll ans=0;
    for(;x<1005;x+=x&-x)ans+=num[p][x];
    return ans;
}
bool check(ll x){
    ll rst=0;
    rep(i,1,1000){
        rst+=x/i*query(i,0);
        rst-=query(i,x%i+1);
    }
    if(rst>=temp)return true;
    return false;
}
int main()
{
    ll t,n,m,z,x,y;
    scanf("%lld",&t);
    while(t--){
        memset(num,0,sizeof(num));
        scanf("%lld%lld",&n,&m);
        ll rst=0;
        rep(i,1,n)scanf("%lld",&a[i]);
        rep(i,1,n)scanf("%lld",&b[i]),rst+=b[i]/a[i],add(a[i],b[i]%a[i],1);
        while(m--){
            scanf("%lld",&z);
            if(z==1){
                scanf("%lld%lld",&x,&y);
                rst-=b[x]/a[x];
                add(a[x],b[x]%a[x],-1);
                add(y,b[x]%y,1);
                a[x]=y;
                rst+=b[x]/y;
            }
            else if(z==2){
                scanf("%lld%lld",&x,&y);
                rst-=b[x]/a[x];
                add(a[x],b[x]%a[x],-1);
                add(a[x],y%a[x],1);
                b[x]=y;
                rst+=y/a[x];
            }
            else{
                scanf("%lld",&x);
                temp=rst+x;
                ll l=1,r=1e13,ans=-1;
                while(l<=r){
                    ll mid=(l+r)>>1;
                    if(check(mid)){
                        r=mid-1;
                        ans=mid;
                    }
                    else{
                        l=mid+1;
                    }
                }
                printf("%lld\n",ans);
            }
        }
    }
    return 0;
}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值