数学+树状数组

Master of Sequence





`


`









这是个向下取整求和问题。
不难想到,应该二分t,但复杂度是O(n*logn)的,有m次询问,总共是O(m * n * logn)。即使给了10s,但是由于hdu的评测机实在不行,
6e9居然10s也跑不完。。,所以得优化一下。这道题需要注意到ai范围只有1000,所以我们可以把相同的ai值一起来求和,优化复杂度。
对于相同的ai值求和,即向下取整求和,关键是处理这个模数问题。所以我们把式子的模数处理出来,[ t-bi/ai ]化为[ k1 * ai+t%ai - ( k2 * ai+bi%ai )/ai ]。
即k1-k2+[ (t%ai-bi%ai)/ai ],记k1=t/ai,k2=bi/ai,c1=t%ai,c2=bi%ai,[ ( c1-c2 )/ai ]当且仅当c1 < c2时为-1,否则为0.故我们只需要每次统计 分母为ai时大于c1的c2个数即可.于是我们用f[ x ][ y ]表示当ai为x时,1 <=bi%ai+1<=y的bi个数。用树状数组来维护这个区间和即可。ai==x的个数就为f[ x ][ 1000 ]。
于是答案就为:

sum为ai==i从1~1000,bi/ai的和。

踩坑:因为树状数组左端点不能为0,不然update会死循环,但是bi%ai可以为0,所以我们统一将bi%ai+1.
踩坑:一开始没用num[ i ]统计ai为i时,ai的个数,用的时getsum(1000)超时了,改了之后是时间限制的一半,怪不得会超时。常数大了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
//两个错误,bi/ai弄错了
//bi%ai在树状数组中不能存0
//getsum()多写了几个超时了... 醉了
/*
* @Author: Marhoosh
* @Date: 2021-10-11 11:00:11
* @Last Modified by: Marhoosh
* @Last Modified time: 2021-10-25 20:49:09
*/
#include<bits/stdc++.h>
using namespace std;
#define Debug(x) cout<<#x<<':'<<x<<endl
#define INF 0x7fffffff
typedef long long ll;
typedef pair<ll,ll> P;
const ll maxn=1e5+11;

ll f[1111][1111],a[maxn],b[maxn],num[1111];
ll n,m,sum;

ll lowbit(ll x){return x & -x;}
void update(ll i,ll x,ll val){for(;x<=1000;f[i][x]+=val,x+=lowbit(x));}
//update,x为0时死循环

ll getsum(ll i,ll x){ll sum=0;for(;x>=1;sum+=f[i][x],x-=lowbit(x));return sum;}

ll check(ll mid,ll k){
ll ans=0;
for(ll i=1;i<=1000;i++){
ans=ans+mid/i*num[i];
ans=ans-num[i]+getsum(i,mid%i+1);
}
ans-=sum;
if(ans>=k) return 1;
else return 0;
}

void solve(ll k){
ll l=0,r=1e9+11;
while(l<r){
ll mid=(l+r)/2;
if(check(mid,k)) r=mid;
else l=mid+1;
}
printf("%lld\n",l);
}

int main(){
ll ca;scanf("%lld",&ca);
while(ca--){
memset(f,0,sizeof(f));memset(num,0,sizeof(num));
sum=0;
scanf("%lld%lld",&n,&m);
for(ll i=1;i<=n;i++) scanf("%lld",&a[i]);
for(ll i=1;i<=n;i++){
scanf("%lld",&b[i]);
sum+=b[i]/a[i];
update(a[i],b[i]%a[i]+1,1);
num[a[i]]++;
}
for(ll i=1;i<=m;i++){
ll type;scanf("%lld",&type);
if(type==1){
ll x,y;scanf("%lld%lld",&x,&y);
sum-=b[x]/a[x];
sum+=b[x]/y;
update(a[x],b[x]%a[x]+1,-1);
num[a[x]]--;
update(y,b[x]%y+1,1);
num[y]++;
a[x]=y;
}
else if(type==2){
ll x,y;scanf("%lld%lld",&x,&y);
sum-=b[x]/a[x];
sum+=y/a[x];
update(a[x],b[x]%a[x]+1,-1);
num[a[x]]--;
update(a[x],y%a[x]+1,1);
num[a[x]]++;
b[x]=y;
}
else{
ll k;scanf("%lld",&k);
solve(k);
}
}
}
return 0;
}