解题思路:
1.对于询问3:我们知道我们要求最小的
t
t
t使得
S
(
t
)
S(t)
S(t)函数刚好超过
K
K
K,这很明显的二分。
2.但是二分的化我们不能暴力去
c
h
e
c
k
check
check求
∑
i
=
1
n
⌊
(
t
−
b
i
)
a
i
⌋
\sum_{i=1}^n\lfloor \frac{(t-b_i)}{a_i} \rfloor
∑i=1n⌊ai(t−bi)⌋
那么我们要考虑优化:
首先我们观察一下就是
a
i
∈
[
1
,
1000
]
a_i\in[1,1000]
ai∈[1,1000]
那么
a
i
a_i
ai就是突破口
虽然有
n
∈
[
1
,
1
e
5
]
n\in[1,1e5]
n∈[1,1e5]个数,但是不同
a
i
a_i
ai个数很少
对于向下取整符号那么我们就可以这么处理
⌊ ( t − b i ) a i ⌋ = ⌊ k 1 ∗ a i + c 1 − k 2 ∗ a i − c 2 a i ⌋ \lfloor \frac{(t-b_i)}{a_i} \rfloor = \lfloor \frac{k_1*a_i+c_1-k_2*a_i-c_2}{a_i} \rfloor ⌊ai(t−bi)⌋=⌊aik1∗ai+c1−k2∗ai−c2⌋
⌊ k 1 ∗ a i + c 1 − k 2 ∗ a i − c 2 a i ⌋ = ⌊ ( k 1 − k 2 ) ∗ a i + c 1 − c 2 a i ⌋ \lfloor \frac{k_1*a_i+c_1-k_2*a_i-c_2}{a_i} \rfloor = \lfloor \frac{(k_1-k_2)*a_i+c_1-c_2}{a_i}\rfloor ⌊aik1∗ai+c1−k2∗ai−c2⌋=⌊ai(k1−k2)∗ai+c1−c2⌋
c
1
=
t
m
o
d
a
i
c_1=t \quad mod \quad a_i
c1=tmodai
c
2
=
b
i
m
o
d
a
i
c_2=b_i\quad mod \quad a_i
c2=bimodai
k
1
=
t
/
a
i
k_1=t/a_i
k1=t/ai
k
2
=
b
i
/
a
i
k_2=b_i/a_i
k2=bi/ai
通过上面的式子我们知道如果
c
1
−
c
2
c_1-c_2
c1−c2是负数的话,那么答案就是
k
1
−
k
2
−
1
k_1-k_2-1
k1−k2−1否则就是
k
1
−
k
2
k_1-k_2
k1−k2
那么我们就可以这样做,因为 a i a_i ai很小,我们可以按照 a i a_i ai对 b i b_i bi进行分类,进行模块化的处理。
对于同一个 a i a_i ai有:
⌊
(
k
1
−
k
2
)
∗
a
i
+
c
1
−
c
2
a
i
⌋
\lfloor \frac{(k_1-k_2)*a_i+c_1-c_2}{a_i}\rfloor
⌊ai(k1−k2)∗ai+c1−c2⌋
我们先把前面拆出来:
⌊
(
k
1
−
k
2
)
∗
a
i
+
c
1
−
c
2
a
i
⌋
=
k
1
−
k
2
+
c
1
−
c
2
a
i
\lfloor \frac{(k_1-k_2)*a_i+c_1-c_2}{a_i}\rfloor=k_1-k_2+\frac{c_1-c_2}{a_i}
⌊ai(k1−k2)∗ai+c1−c2⌋=k1−k2+aic1−c2
前面是可以直接暴力求的对于二分出来的
t
t
t直接套就可以求出
k
1
和
k
2
k_1和k_2
k1和k2
对于同一个
a
i
a_i
ai和二分出来的
t
t
t我们要算的就是有多少个
c
2
>
c
1
c_2>c_1
c2>c1,因为这个的贡献是
−
1
-1
−1,对于这个我们很容易想到树状数组。我们先把每个
c
2
c_2
c2插入树状数组中,对于每个
c
1
c_1
c1查询有多少个比
c
1
c_1
c1小的用总数减掉就行!
还有一个特殊情况就是 b i m o d a i = = 0 bi \quad mod \quad a_i==0 bimodai==0,这个时候是不插入树状数组的,因此我们还要另外开一个数组去记录树状数组里面插入了多少个数。
实现细节看代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e3 + 10;
const int N = 1e5 + 10;
ll tr[maxn][maxn];//对于每个ai开一个树状数组
ll Count[maxn], num[maxn];//Count记录有多少个数插入了树状数组,num记录出现的ai,就是每个ai出现的次数
inline int lowbit(int x) {
return x & (-x);
}
inline void add(int id, int x, int val) {
num[id]+=val;
if(!x) return;
Count[id] += val;
while(x < maxn) {
tr[id][x] += val;
x += lowbit(x);
}
}
inline ll sum(int id, int x) {
ll res = 0;
while(x) {
res += tr[id][x];
x -= lowbit(x);
}
return res;
}
ll n, m, k;
ll a[N], b[N];
ll ans = 0;
bool check(ll mid) {
ll res = 0;
for(int i = 1; i <= 1000; ++ i) {
res += (mid / i) * num[i];//求解k1,因为二分出来的mid就是t,t是不变的t/ai*(ai的个数),就是结果的一部分
res -= Count[i] - sum(i,mid%i);//减去贡献为-1的
}
return k <= ans + res;
}
int main() {
ios::sync_with_stdio(0);
cout.tie(0);
cout.tie(0);
//...................
int T;
cin >> T;
while(T --) {
memset(tr,0,sizeof(tr));
memset(num,0,sizeof(num));
memset(Count,0,sizeof(Count));
ans = 0;
cin >> n >> m;
for(int i = 1; i <= n; ++ i) cin >> a[i];
for(int i = 1; i <= n; ++ i) {
cin >> b[i];
ans += -b[i]/a[i];//ans是k2
add(a[i],b[i]%a[i],1);
}
while(m --) {
int op, x, y;
cin >> op;
if(op == 1) {
cin >> x >> y;
ans -= -b[x]/a[x];
add(a[x],b[x]%a[x],-1);
a[x] = y;
ans += -b[x]/a[x];
add(a[x],b[x]%a[x],1);
} else if(op == 2) {
cin >> x >> y;
ans -= -b[x]/a[x];
add(a[x],b[x]%a[x],-1);
b[x] = y;
ans += -b[x]/a[x];
add(a[x],b[x]%a[x],1);
} else {
cin >> k;
int l = 0, r = 1e9 + 10;
while(l < r) {
int mid = (l + r) >> 1;
if(check(mid)) r = mid;
else l = mid + 1;
}
cout << l << "\n";
}
}
}
return 0;
}
/*
1
4 6
2 4 6 8
1 3 5 7
1 2 3
2 3 3
3 15
1 3 8
3 90
3 66
1
4 1
2 3 6 8
1 3 3 7
3 15
*/