题面:
得分情况:
40分,写了纯暴力。
正解:
其实
O(nn−−√logn)
O
(
n
n
log
n
)
的做法还挺好想的(然而我在考场上并没有想出来),分块莫队再用线段树维护块中类型的最大值即可。我们要想办法把这个
logn
log
n
去掉。经过冷静地分析,我们发现,修改的总次数为
nn−−√
n
n
,查询的总次数为
n
n
,如果我们能够将前者的复杂度降为,后者的复杂度升为
O(n−−√)
O
(
n
)
,就可以去掉那个
logn
log
n
了。
我们先将l和r差很小的答案暴力计算出来,将剩下的询问分块,每个块内按r排序,这样就可以保证右端只增不减,至于左端因为都在一个块内,
n−−√
n
暴力维护即可。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+100;
const int bs=300;
int n,m,q,a[maxn],b[maxn],blk[maxn],bl[maxn/bs+10],br[maxn/bs+10];
int ql[maxn],qr[maxn],qx[maxn],qy[maxn];
long long bmx[maxn/bs+10],tmp[maxn/bs+10],cnt[maxn],ans[maxn];
vector <int> qq[maxn];
void inc(int x,int y) { cnt[x]+=y;if(cnt[x]>bmx[blk[x]]) bmx[blk[x]]=cnt[x]; }
long long query(int x,int y)
{
long long nowans=0;
for(;x<=y&&x!=bl[blk[x]];x++) nowans=max(nowans,cnt[x]);
for(;x<=y&&y!=br[blk[y]];y--) nowans=max(nowans,cnt[y]);
for(;x<=y;x=br[blk[x]]+1) nowans=max(nowans,bmx[blk[x]]);
return nowans;
}
bool cmp(int x,int y)
{
return qr[x]<qr[y];
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) scanf("%d",&b[i]);
for(int i=0;i<=maxn-1;i++)
{
blk[i]=i/bs;
if(!bl[i/bs]) bl[i/bs]=i;
br[i/bs]=i;
}
for(int i=1;i<=q;i++)
{
scanf("%d%d%d%d",&ql[i],&qr[i],&qx[i],&qy[i]);
if(qr[i]-ql[i]+1<=bs)
{
memset(bmx,0,sizeof(bmx));
for(int j=ql[i];j<=qr[i];j++) inc(a[j],b[j]);
ans[i]=query(qx[i],qy[i]);
for(int j=ql[i];j<=qr[i];j++) cnt[a[j]]-=b[j];
}
else qq[blk[ql[i]]].push_back(i);
}
for(int i=0;i<maxn;i++)
{
if(!qq[i].size()) continue;
sort(qq[i].begin(),qq[i].end(),cmp);
int last=br[i];
memset(cnt,0,sizeof(cnt));memset(bmx,0,sizeof(bmx));
for(int j=0;j<qq[i].size();j++)
{
int x=qq[i][j];
while(last<qr[x])
{
last++;
inc(a[last],b[last]);
}
for(int k=0;k<maxn/bs+10;k++) tmp[k]=bmx[k];
for(int k=ql[x];k<=br[i];k++) inc(a[k],b[k]);
ans[x]=query(qx[x],qy[x]);
for(int k=ql[x];k<=br[i];k++) cnt[a[k]]-=b[k];
for(int k=0;k<maxn/bs+10;k++) bmx[k]=tmp[k];
}
}
for(int i=1;i<=q;i++) printf("%lld\n",ans[i]);
return 0;
}