CF2025G Variable Damage 题解
将英雄视为序列 \(x\),神器视为序列 \(y\),题意可以转化为维护 \(x,y\) 两个序列,支持 \(q\) 次某一个序列插入,查询对于每一个 \(y\) 选择一个未配对的 \(x\) 与其配对后 \(\min(x,y)\) 的和的最大值再加上 \(x\) 序列中的所有数的和。
\(x\) 序列中的所有数的和直接维护就行,考虑如何计算配对后 \(\min(x,y)\) 的和。
\(\min(x,y)\) 的和的最大值是好求的,把 \(x,y\) 分别排序后从大到小一一配对就是最大值。
考虑离线,把 \(x,y\) 按照值降序排序,加入时就插入序列中的对应位置。考虑把 \(x\) 中的元素记为 \(+1\),\(y\) 中的元素记为 \(-1\)。
考虑维护前缀和。如果 \(x\) 元素对应的前缀和 \(\le 0\),证明可以匹配,由于降序排序,贡献一对 \(\min(x,y)=x\);如果 \(y\) 元素对应的前缀和 \(\ge 0\),证明可以匹配,由于降序排序,贡献一对 \(\min(x,y)=y\)。
插入时是单点修改,对前缀和的影响是区间修改,转化后的查询是全局小于等于(或大于等于)某数查询,是经典的分块问题。修改打块加标记,块内排序,暴力重构,块内二分查询即可。
时间复杂度 \(O(q\sqrt{q\log q})\) 过不去,考虑把排序换成归并排序。注意到块加标记可以独立维护,维护一个桶表示块内元素序列中前缀和为某数的贡献,做前缀和查询就可以 \(O(\sqrt{q})\)。桶和前缀和暴力重构的时候顺便维护。时间复杂度 \(O(q\sqrt{q})\)。
空间复杂度 \(O(q\sqrt{q})\) 过不去,考虑修改是后缀加,且每个位置只会被修改一次,因此块内不同元素数量为 \(\sqrt{q}\) 级别的。暴力重构时强制块内所有元素减去最小值,加到加法标记上,空间复杂度就可以优化的 \(O(q)\)。可以通过。
本题卡常,三分调块长后我的最优块长为 \(300\)。另外代码中的排序是升序排序,所以前缀和就变成了后缀和。
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
struct val
{
long long v,p,t;
}a[400000],b1[400000],b2[400000];
long long q,n=0,k,id[400000],t[2][1600][400],s[2][1600][400],ad[400000],op[400000],x[400000],sum=0;
pair<long long,int>c[400000];
inline long long read()
{
long long x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
return x*f;
}
void pushup(long long x)
{
ad[x]+=a[(x-1)*k+1].v;
for(int i=0;i<=k;i++)t[0][x][i]=s[0][x][i]=t[1][x][i]=s[1][x][i]=0;
for(int i=(x-1)*k+2;i<=x*k;i++)
{
a[i].v-=a[(x-1)*k+1].v;
if(a[i].t)t[a[i].t-1][x][a[i].v]+=c[a[i].p].first;
}
a[(x-1)*k+1].v=0;
if(a[(x-1)*k+1].t)t[a[(x-1)*k+1].t-1][x][a[(x-1)*k+1].v]+=c[a[(x-1)*k+1].p].first;
s[0][x][0]=t[0][x][0],s[1][x][0]=t[1][x][0];
for(int i=1;i<=k;i++)s[0][x][i]=s[0][x][i-1]+t[0][x][i],s[1][x][i]=s[1][x][i-1]+t[1][x][i];
}
void rebuild(long long x,long long l,long long r,long long c)
{
long long p1=0,p2=0,n1=1,n2=1;
for(int i=(x-1)*k+1;i<=x*k;i++)
if(a[i].p>=l&&a[i].p<=r)a[i].v+=c,b1[++p1]=a[i];
else b2[++p2]=a[i];
for(int i=(x-1)*k+1;i<=x*k;i++)
if((n1<=p1&&b1[n1].v<=b2[n2].v)||(n2>p2))a[i]=b1[n1],n1++;
else a[i]=b2[n2],n2++;
pushup(x);
}
void add(long long l,long long r,long long c)
{
long long p=id[l],q=id[r];
if(p==q)rebuild(p,l,r,c);
else
{
rebuild(p,l,p*k,c),rebuild(q,(q-1)*k+1,r,c);
for(int i=p+1;i<=q-1;i++)ad[i]+=c;
}
}
int main()
{
q=read();
for(int i=1;i<=q;i++)op[i]=read(),x[i]=read(),c[++n]=make_pair(x[i],i);
sort(c+1,c+n+1),k=300;
for(int i=1;i<=n;i++)id[i]=(i-1)/k+1,a[i].p=i;
for(int i=1;i<=q;i++)
{
x[i]=lower_bound(c+1,c+n+1,make_pair(x[i],i))-c;
for(int j=(id[x[i]]-1)*k+1;j<=id[x[i]]*k;j++)
if(a[j].p==x[i])a[j].t=op[i];
if(op[i]==1)add(1,x[i],1),sum+=c[x[i]].first;
else add(1,x[i],-1);
long long ans=0;
for(int j=1;j<=id[n];j++)
{
long long p=max(-1ll,min(k+1,-ad[j]));
if(p>k)ans+=s[0][j][k];
else if(p>=0)ans+=s[0][j][p];
if(p<=k)
{
if(p>0)ans+=(s[1][j][k]-s[1][j][p-1]);
else ans+=s[1][j][k];
}
}
printf("%lld\n",ans+sum);
}
return 0;
}

浙公网安备 33010602011771号