CF1618G Trader Problem
wangzhiyuan123 · · 题解
写出来感觉和csp-j2021 T4差不多。
具体实现方法:将两个数组放在一起排序后求前缀和,维护一个链表,链表除了记录前一块和后一块之外,还要统计当前块的头和尾。用堆维护两个块之间的距离的集合,对问题离线。
设当前块有
当前查询的值大于两个块的距离时,统计答案的变量减去当前两个块的值,合并这两个块,再加上这两个块的值。
具体实现见代码注释。
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,al,q,ans;
int as[400005];
int qzhv[800005],qzhc[800005];
struct dt{
int val,c;
}d[800005];
struct qst{//存储问题以离线
int rk,qv;
}qs[400005];
struct blk{//链表
int lst,nxt;
int frm,to;
}k[800005];
dt make_dt(int v,int f){
dt xf;
xf.val=v;xf.c=f;
return xf;
}
bool cmp(dt a1,dt a2){return a1.val<a2.val;}
bool qcmp(qst a1,qst a2){return a1.qv<a2.qv;}
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > pq;//用以统计相邻块的间隙
#define gtblock(x) (qzhv[k[x].frm]-qzhv[max(0ll,k[x].frm-qzhc[k[x].frm]+qzhc[k[x].to-1])])//求当前块的最大值
main(){
scanf("%lld%lld%lld",&n,&m,&q);
for(int i=1,u;i<=n;i++){
scanf("%lld",&u);
d[i]=make_dt(u,1);
}
for(int i=1,u;i<=m;i++){
scanf("%lld",&u);
d[i+n]=make_dt(u,0);
}
al=n+m;
sort(d+1,d+1+al,cmp);
for(int i=1;i<=al;i++){//初始每个块只包含一个元素
k[i].frm=k[i].to=i;
qzhc[i]=qzhc[i-1]+d[i].c;
qzhv[i]=qzhv[i-1]+d[i].val;//有了前缀和以及每个块统计的头和尾后,可以快速求出块里原来a数组的数的个数和最大的一段
ans+=d[i].c*d[i].val;
}
for(int i=1;i<al;i++){
pq.push(make_pair(d[i+1].val-d[i].val,i));//把这个间隙丢进堆里
k[i].nxt=i%al+1;//连接链表
k[i%al+1].lst=i;
}
for(int i=1;i<=q;i++) scanf("%lld",&qs[i].qv),qs[i].rk=i;
sort(qs+1,qs+1+q,qcmp);
for(int i=1;i<=q;i++){
while(!pq.empty()&&pq.top().first<=qs[i].qv){
int x=pq.top().second;
pq.pop();
ans-=gtblock(x);
ans-=gtblock(k[x].nxt);
k[k[x].lst].nxt=k[x].nxt;
k[k[x].nxt].lst=k[x].lst;
k[k[x].nxt].to=k[x].to;
ans+=gtblock(k[x].nxt);
}
as[qs[i].rk]=ans;
}
for(int i=1;i<=q;i++) printf("%lld\n",as[i]);
}