题面简化
有
A
A
A 和
B
B
B 两个序列,
\ {}
\ {}
\ {}
\ {}
\ {}
长度分别为
n
n
n
(
n
<
250
)
(n<250)
(n<250) ,
m
m
m
(
m
<
100000
)
(m<100000)
(m<100000)
\ {}
\ {}
\ {}
\ {}
\ {}
(
a
i
<
2000
(a_i<2000
(ai<2000 ,
b
i
<
1000000
)
b_i<1000000)
bi<1000000)
q
q
q
(
q
≤
100000
)
(q≤100000)
(q≤100000) 次操作:
\ {}
\ {}
\ {}
\ {}
\ {}
更改一个
a
a
a 或
b
b
b 的值,
\ {}
\ {}
\ {}
\ {}
\ {}
或查询
a
i
(
l
1
≤
i
≤
r
1
)
∗
b
j
(
l
2
≤
j
≤
r
2
)
a_i(l_1≤i≤r_1)*b_j(l_2≤j≤r_2)
ai(l1≤i≤r1)∗bj(l2≤j≤r2)的第
K
K
K大值。
\ {}
\ {}
\ {}
\ {}
\ {}
\ {}
\ {}
\ {}
\ {}
\ {}
查询次数小于200。
时间限制
\ {} \ {} \ {} \ {} \ {} 4 s 4s 4s
思路
看到 q n m < 4 ∗ 1 0 11 qnm<4*10^{11} qnm<4∗1011 ,直接爆炸,不可能暴力。(废话)
想想有什么方法可以求第
K
K
K大值?
未排序时用快排的二分思想 ,
O
(
n
)
O(n)
O(n)的复杂度
已排序时直接输出
其实上面的都用不上,
这题是让你算所有乘积的第
K
K
K大值,并没有把所有可能的值都给你,
而暴力求值会超时,
怎么办?
猜答案!
上面的二分其实就为我们提供了思路,
用二分先猜答案,然后检验猜的答案是大了还是小了,循环往复,直到正确,
这就是二分答案。
解决
一、充分运用数据
我们看到
n
<
250
n<250
n<250,和
m
m
m 的范围不一样,这不明摆这用来枚举吗?
所以枚举时求的是
b
[
i
]
b[i]
b[i] 满足
b
[
i
]
<
x
/
a
[
i
]
b[i]<x/a[i]
b[i]<x/a[i] 的个数总和,
\ {}
\ {}
\ {}
\ {}
\ {}
这是区间求和,所以再加上一个简单的前缀和。
代码实现
#include <bits/stdc++.h>
using namespace std;
int n,m,a[300],b[100010],q,cnt[1000006];
int l1,r1,l2,r2,k;
int l,r,mid;
inline int read(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
inline int check(int x,int l,int r){
int res=0;
for(int i=l;i<=r;++i){
if(x/a[i]>1000000) continue;
res+=cnt[1000000]-cnt[x/a[i]];
}
return res;
}
int main(){
n=read();m=read();
for(int i=1;i<=n;++i) a[i]=read();
for(int i=1;i<=m;++i) b[i]=read();
q=read();
while(q--){
int op=read();
if(!op){
int type=read(),x=read(),y=read();
if(type) b[x]=y;else a[x]=y;
}else{
l1=read();r1=read();l2=read();r2=read();k=read();
memset(cnt,0,sizeof(cnt));
for(int i=l2;i<=r2;++i) cnt[b[i]]++;
for(int i=1;i<=1000000;++i) cnt[i]+=cnt[i-1];
l=1;r=2000000000;
while(l<=r){
mid=l+r>>1;
if(check(mid,l1,r1)>=k)l=mid+1;
else r=mid-1;
}
printf("%d\n",r+1);
}
}
return 0;
}
二、无视数据
这种做法和一相差无几,主要是check函数的差异
可以在check函数中再套一个二分答案,无视了题中的小数据,
可具有更低的时间复杂度
代码实现
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,k,ks,x,y,z,a[100039],b[100039],c[100039],d[100039],sx,flag,ans,ls,rs,ch,dh;
unsigned int l,r,mid;
inline int check(int x){
register int i,ls,rs,mid;ans=0;
for(i=1;i<=ch;i++){
ls=0;rs=dh+1;
while(ls+1<rs){
mid=ls+rs>>1;
if(c[i]*d[mid]>x) rs=mid;
else ls=mid;
}
ans+=ls;
if(ans>=z) return 0;
}
return 1;
}
int main(){
register int i,j;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++) scanf("%d",&a[i]);
for(i=1;i<=m;i++) scanf("%d",&b[i]);
scanf("%d",&k);
while(k--){
scanf("%d",&sx);
if(!sx){
scanf("%d",&sx);
if(sx==0) scanf("%d%d",&x,&y),a[x]=y;
else scanf("%d%d",&x,&y),b[x]=y;
}
else {
scanf("%d%d%d%d%d",&x,&y,&ls,&rs,&z);
z=(rs-ls+1)*(y-x+1)-z+1;
ch=dh=0;
for(j=x;j<=y;j++) c[++ch]=a[j];
for(j=ls;j<=rs;j++) d[++dh]=b[j];
sort(d+1,d+dh+1);
l=0;r=2e9+7;
while(l+1<r){
mid=(l+r)>>1;
if(check(mid)) l=mid;
else r=mid;
}
printf("%d\n",r);
}
}
}