JZOJ 5439. 【NOIP2017提高A组集训10.31】Calculate

本文探讨了整除运算在算法实现中的难点,特别是在C++中处理负数时的特殊情况。通过对特定数学表达式的拆解,提出了有效的算法优化策略,并通过实例代码展示了如何维护关键变量以提高效率。

题目

这里写图片描述
数据范围
T5n106m104k109
0Bi1091Ai103

题解

十分要注意的是,C++中的整除是向0取整,所以负数会有问题!!!
所以要用实数和floor。

题目条件

①由T来控制S(T),S(T)随T的增大而不递减。
②要求满足条件的最小的T。
AiBi会变。
困惑的地方:①是整除,式子不会拆开。②A不一样。

具体做法

有什么发现:A的范围很小。所以可以将他们分类。
对于每个x,假设有k个数Ak1=Ak2=Ak3=...=Akk=x,求Ai=x对答案的贡献。

S=Σki=1TBkix=

现在就是这里拆不开。
经发现,这个东西等于
Σki=1TxBkix[T%x<Bki%x]

请记住它。
那么设t为这k个数中Bki>T%x的个数。

S=Σki=1TxBkixt

所以只用维护Σki=1Bkix和mod p>x的具体个数。

若要修改AxBx,就要修改相应的
Σki=1Bkix和mod p>x的具体个数。

总结

这道题最重要的是要会拆那个式子,从而维护必要的值。见上面铜色部分。

题解

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 100010
#define M 1010
#define LL long long
#define P(a) putchar(a) 
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
struct note{
    LL op,x,y,id;
};note qu[N];
LL i,j,k,l,r,n,m,S,T;
LL op,x,y,delta;
LL L,R,mid,ans,last,temp,mx;
LL a[N],b[N];
LL a1[M][M];
LL cz[M][2];
bool pp;
LL read(){
    LL res=0,fh=1;char ch;
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')fh=-1,ch=getchar();
    while(ch>='0'&&ch<='9')res=res*10+ch-'0',ch=getchar();
    return res*fh;
}
void write(LL x){
    if(x>9)write(x/10);
    P(x%10+'0');
}
bool cmp(note x,note y){return x.x<y.x;}
int main(){
    T=read();
    while(T--){
        memset(a1,0,sizeof(a1));
        memset(cz,0,sizeof(cz));
        n=read();m=read();
        fo(i,1,n)a[i]=read();
        mx=0;
        fo(i,1,n){
            b[i]=read();
            cz[a[i]][0]+=b[i]/a[i];
            cz[a[i]][1]++;
            mx=max(mx,a[i]);
            temp=b[i]%a[i];
            a1[a[i]][temp]++;
        }
        fo(i,1,mx)fo(j,1,i)a1[i][j]+=a1[i][j-1];
        fo(i,1,m){
            qu[i].op=read();
            if(qu[i].op<3){
                qu[i].x=read(),qu[i].y=read();
            } else qu[i].x=read();
            qu[i].id=i;
        }
        fo(i,1,m){
            if(qu[i].op==3){
                L=0,ans=R=1000000000000;
                while(L<R){
                    mid=(L+R)>>1;
                    S=0;
                    fo(j,1,mx)S+=cz[j][1]*(mid/j)-cz[j][0]-a1[j][j]+a1[j][mid%j];
                    if(S>=qu[i].x)ans=R=mid;else L=mid+1;
                }
                write(ans),P('\n');
            } else
            if(qu[i].op==1){
                x=qu[i].x,y=qu[i].y;
                cz[a[x]][0]-=b[x]/a[x];cz[a[x]][1]--;
                fo(j,b[x]%a[x],a[x])a1[a[x]][j]--;
                a[x]=y;mx=max(mx,y);
                cz[a[x]][0]+=b[x]/a[x];cz[a[x]][1]++;
                fo(j,b[x]%a[x],a[x])a1[a[x]][j]++;
            } else{
                x=qu[i].x,y=qu[i].y;
                cz[a[x]][0]+=y/a[x]-b[x]/a[x];
                l=b[x]%a[x],r=y%a[x],delta=-1;
                if(l>r)swap(l,r),delta=1;
                fo(j,l,r-1)a1[a[x]][j]+=delta;
                b[x]=y;
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值