hdu6274(向下取整式子分解,二分判断)
题:http://acm.hdu.edu.cn/showproblem.php?pid=6274
题意:给定n(n<=1e5)的a[i]和b[i](a[i]<=1e3,b[i]<=1e9) 有m个操作操作一[x,y]:a[x]=y; 操作二[x,y]:b[x]=y;操作三[k]:输出最小的 x 使得S(x)>=k( S(x) =Σ(x-b[i])/a[i] )
分析:要求最小的,我们考虑对每个询问二分,因为题目保证操作三不会超过1e3次且x越大S(x)越能满足,满足单调性;
关键在于S(x)的拆分,如果x,b[i]都整除a[i]的话,我们就可以拆分成x/a[i] 和b[i]/a[i] ,但是结果肯定不可能都是这样的;
那么我们就先把这部分答案存进来,再来考虑不整除的情况,那就考虑取模x%a[i]和b[i]%a[i];
这里假设x>b[i],若前者小于后者则相当于(x-b[i])/a[i]在直接算(x/a[i]-b[i]/a[i])少了1的贡献的(可以理解为在moda[i]意义下,他们之间间隔没有一个a[i]的长度)
所以我们直接算sum(b[i]/a[i]),在二分x去算(x/a[i])和可能少了1的部分贡献(用树状数组去维护有多少个是大于(x%a[i])的);
这里check里面,我们只要枚举余数即可,也就是说1e3次询问三复杂度为1e3*1e3*log1e9 ,10s时限没问题
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int M=1e5+5; const int N=1e3+3; int a[M],b[M],num[M]; int n,m; struct BIT{ int tr[N+3]; void add(int i,int v){ i++; for(;i<N;i+=i&-i) tr[i]+=v; } int sum(int x){ int res=0; x++; for(int i=x;i;i-=i&-i) res+=tr[i]; return res; } }bit[N]; bool check(ll x,ll limit){ ll res=0; ///枚举余数 for(int i=1;i<=1000;i++){ res+=(x/i)*num[i]-(num[i]-bit[i].sum(x%i)); if (res>=limit) return 1; } return res>=limit; } int main(){ int T; scanf("%d",&T); while(T--){ scanf("%d%d",&n,&m); ll sum=0; memset(num,0,sizeof(num)); memset(bit,0,sizeof(bit)); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++){ scanf("%d",&b[i]); sum+=b[i]/a[i]; bit[a[i]].add(b[i]%a[i],1); num[a[i]]++; } while(m--){ int op,x,y; scanf("%d",&op); if(op==1){ scanf("%d%d",&x,&y); sum+=b[x]/y-b[x]/a[x]; bit[a[x]].add(b[x]%a[x],-1); bit[y].add(b[x]%y,1); num[a[x]]--,num[y]++; a[x]=y; } else if(op==2){ scanf("%d%d",&x,&y); sum+=y/a[x]-b[x]/a[x]; bit[a[x]].add(b[x]%a[x],-1); bit[a[x]].add(y%a[x],1); b[x]=y; } else{ ll k; scanf("%lld",&k); ll l=0,r=2e12,ans; ll limit=k+sum; while(l<=r){ ll midd=(l+r)>>1; if(check(midd,limit)){ ans=midd; r=midd-1; } else l=midd+1; } printf("%lld\n",ans); } } } return 0; }