题目

- 转载请注明地址哦
- 题意很简单,就是让你算最小的t,使得S(t)>=k,或者更新a/b数组
- 17杭州CCPC的题,感觉还是挺好的一道题,优化过程非常循序渐进,也不是特别难,但需要耐心和思考
- 简单说一下这道题的优化过程
1
- 题意很好懂,先看能不能暴力,发现不行,每次算S(t)S(t)都要nn次,从小到大枚举t又有可能要枚举1e12+1e9次,显然会T
2
- 经过观察发现S(t)S(t)单调,想到二分,枚举tt大概只需要40多次,但每次算S(t)需要跑n便,复杂度O(nlogn),会T,继续优化
3
- 看样例数据发现,a的范围1~1000,将S(t)转换成和a有关的式子然后跑1000的循环仿佛可行
- 设t=pa+q,b=ma+nt=pa+q,b=ma+n,则S(t)=t/a1+b1/a1+(q1<n1)?(−1):0+t/a2+b2/a2+(q2<n2)?(−1):0+t/a3+b3/a3+(q3<n3)?(−1):0+...+t/an+bn/an+(qn<nn)?(−1):0S(t)=t/a1+b1/a1+(q1<n1)?(−1):0+t/a2+b2/a2+(q2<n2)?(−1):0+t/a3+b3/a3+(q3<n3)?(−1):0+...+t/an+bn/an+(qn<nn)?(−1):0
- 这里的”/”是整除
- 对于b1/a1+b2/a2+...+bn/anb1/a1+b2/a2+...+bn/an,可在输入时算好,对于更新,只需要减去原来的值,在加上新的bi‘/a′ibi‘/ai′即可
- 因为a的范围小于1000,所以可以用1000的数组统计每个值出现的次数,从1到1000跑一遍可以算出t/a1+t/a2+...+t/ant/a1+t/a2+...+t/an
4
- 开始考虑余数问题,因为余数的不同情况也会对答案产生贡献,所以需要记录,当a=aia=ai是,大于t%ait%ai的个数
- 对每个a,记录余数分部情况,统计总个数,复杂度O(1000∗1000)O(1000∗1000),会T
5
- 统计前缀个数,可以用树状数组维护,1000个一维的树状数组
- 查询总复杂度O(40∗1000∗log1000)O(40∗1000∗log1000)
代码
# include <iostream>
# include <stdio.h>
# include <string.h>
# include <algorithm>
# include <math.h>
# include <queue>
# include <string>
# include <vector>
# include <set>
# include <map>
# define INF 0x3f3f3f3f
# define Inf 0x7f7f7f7f
# define clr0(x) memset(x,0,sizeof(x))
# define clr1(x) memset(x,INF,sizeof(x))
# define clrf(x) memset(x,-1,sizeof(x))
# define rep(i,a) for(int i = 0;i<(a);i++)
# define repf(i,a,b) for(int i = (a);i<=(b);i++)
# define repu(i,a,b) for(int i = (a);i<(b);i++)
# define repd(i,a,b) for(int i = (a);i>=(b);i--)
# define lowbit(x) ((x)&(-x))
# define ww(a) while(a)
# define sc(x) scanf("%d",&(x))
# define sl(x) scanf("%lld",&(x)
# define pc(x) printf("%d\n",(x));
# define pl(x) printf("%lld\n",(x));
# define scf scanf
# define prf printf
# define wT() int T;scanf("%d",&T);while(T--)
# define wt() int T;scanf("%d",&T);for(int tt = 1;tt<=T;tt++)
# define lson(x) (x)<<1
# define rson(x) (x)<<1|1
# define ll long long
# define fuckio ios::sync_with_stdio(false);
# define fuck cout<<"fuck:"<<__LINE__<<endl;
# define maxn 200020
using namespace std;
ll A[1020][1020];
ll num[1020];
ll aa[100020];
ll bb[100020];
ll z;
int n,m;
int add(int x,int y,ll val)
{
while(y<=1000){
A[x][y] += val;
y += lowbit(y);
}
}
ll sum(int x,int y)
{
ll ret = 0;
while(y){
ret += A[x][y];
y -= lowbit(y);
}
return ret;
}
ll funcS(ll t)
{
ll ret = 0;
ll yu;
repf(a,1,1000){
ret += num[a]*(t/a);
yu = t%a+1;
int nn = sum(a,1000)-sum(a,yu);
ret -= nn;
}
return ret;
}
int tmp;
int tmpx,tmpy;
int main()
{
fuckio
wT(){
clr0(A);
clr0(num);
ll z = 0;
scanf("%d%d",&n,&m);
repf(i,1,n){
sc(aa[i]);num[aa[i]]++;
}
repf(i,1,n){
sc(bb[i]);z += bb[i]/aa[i];
add(aa[i],(bb[i]%aa[i]+1),1);
}
ww(m--){
sc(tmp);
if(tmp == 1){
sc(tmpx);sc(tmpy);
num[aa[tmpx]]--;
add(aa[tmpx],(bb[tmpx]%aa[tmpx]+1),-1);
z -= bb[tmpx]/aa[tmpx];
aa[tmpx] = tmpy;
z += bb[tmpx]/aa[tmpx];
add(aa[tmpx],(bb[tmpx]%aa[tmpx]+1),1);
num[aa[tmpx]]++;
}
else if(tmp == 2){
sc(tmpx);sc(tmpy);
add(aa[tmpx],(bb[tmpx]%aa[tmpx]+1),-1);
z -= bb[tmpx]/aa[tmpx];
bb[tmpx] = tmpy;
z += bb[tmpx]/aa[tmpx];
add(aa[tmpx],(bb[tmpx]%aa[tmpx]+1),1);
}
else if(tmp == 3){
ll k;
scanf("%lld",&k);
k += z;
ll high = 1e11;
ll low = 0;
ll mid;
ww(low<=high){
mid = (low+high)/2;
if(funcS(mid)>=k){
high = mid-1;
}
else{
low = mid+1;
}
}
prf("%lld\n",high+1);
}
}
}
}