题目
题目链接:https://codeforces.com/contest/702/problem/F
有 \(n\) 种T恤,每种有价格 \(c_i\) 和品质 \(q_i\)。
有 \(m\) 个人要买 \(T\) 恤,第 \(i\) 个人有 \(v_i\) 元,每人每次都会买一件能买得起的 \(q_i\) 最大的T恤。一个人只能买一种T恤一件,所有人之间都是独立的。
问最后每个人买了多少件 \(T\) 恤?如果有多个 \(q_i\) 最大的T恤,会从价格低的开始买。
\(n,m\leq 2\times 10^5\)。
思路
可以先把所有物品排序,如果把每一个人分别考虑复杂度显然是无法优化的。
所以我们换一个角度考虑,维护每一个人剩余的钱,然后枚举每一件衣服,把剩余的钱不小于衣服价格的人的钱全部减去衣服价格。
这样看起来就可以用数据结构维护了。我们需要支持把超过 \(c\) 的部分减去 \(c\) 的操作。但是如果每次暴力把超过 \(c\) 的部分一个一个减去,复杂度依然爆炸。
根据价格 \(c\) 把所有人分成三部分:剩余的钱在 \([0,c)\),\([c,2c]\),\((2c,+\infty)\) 的。其中第一部分不需要操作,第三部分需要支持区间打标记,第二部分就只能暴力减。
考虑维护一棵 FHQ Treap。这样就可以轻松的把三个部分划出来,并且支持取件打标记或者暴力减。考虑到第二部分每一个数减去 \(c\) 后至少都除以了 \(2\),所以每一个数字最多只会被暴力减 \(O(\log a)\) 次,这样暴力插入的复杂度就是 \(O(n\log^2m)\) 的了。
具体实现时先把三个部分拿出来,然后给第三部分全部打上钱减 \(c\),买的衣服 \(+1\) 的两个标记,然后与第一部分合并(显然第三部分全部减去 \(c\) 后每一个人的钱都>第一部分的钱),最后把第二部分依次插入即可。
时间复杂度 \(O(n\log^2 m)\)。
代码
#include <bits/stdc++.h>
#define random (rand()*32768+rand())
using namespace std;
const int N=200010;
int n,m,rt,ans[N];
struct node
{
int c,q;
}a[N];
bool cmp(node x,node y)
{
return (x.q==y.q) ? (x.c<y.c) : (x.q>y.q);
}
struct FHQTreap
{
int val[N],cnt[N],lazy[N][2],dat[N],ch[N][2];
void pushdown(int x)
{
int lc=ch[x][0],rc=ch[x][1];
if (lazy[x][0])
{
cnt[lc]+=lazy[x][0]; lazy[lc][0]+=lazy[x][0];
cnt[rc]+=lazy[x][0]; lazy[rc][0]+=lazy[x][0];
lazy[x][0]=0;
}
if (lazy[x][1])
{
val[lc]-=lazy[x][1]; lazy[lc][1]+=lazy[x][1];
val[rc]-=lazy[x][1]; lazy[rc][1]+=lazy[x][1];
lazy[x][1]=0;
}
}
void split(int x,int k,int &lc,int &rc)
{
if (!x) return (void)(lc=rc=0);
pushdown(x);
if (val[x]<=k)
lc=x,split(ch[x][1],k,ch[x][1],rc);
else
rc=x,split(ch[x][0],k,lc,ch[x][0]);
}
int merge(int x,int y)
{
if (!x || !y) return x+y;
pushdown(x); pushdown(y);
if (dat[x]<dat[y])
return ch[x][1]=merge(ch[x][1],y),x;
else
return ch[y][0]=merge(x,ch[y][0]),y;
}
void ins(int x,int v)
{
int y,z;
val[x]=v; dat[x]=random;
split(rt,v,y,z);
rt=merge(merge(y,x),z);
}
void dfs(int x,int &y,int c,bool flag)
{
pushdown(x);
if (ch[x][0]) dfs(ch[x][0],y,c,flag);
if (ch[x][1]) dfs(ch[x][1],y,c,flag);
if (flag)
{
int z;
val[x]-=c; cnt[x]++; ch[x][0]=ch[x][1]=0;
split(y,val[x],y,z);
y=merge(merge(y,x),z);
}
}
}fhq;
int main()
{
srand(123123);
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d%d",&a[i].c,&a[i].q);
scanf("%d",&m);
for (int i=1,x;i<=m;i++)
{
scanf("%d",&x);
fhq.ins(i,x);
}
sort(a+1,a+1+n,cmp);
for (int i=1;i<=n;i++)
{
int x,y,z,c=a[i].c;
fhq.split(rt,c-1,x,y); fhq.split(y,c*2,y,z);
fhq.cnt[z]++; fhq.lazy[z][0]++;
fhq.val[z]-=c; fhq.lazy[z][1]+=c;
rt=fhq.merge(x,z);
fhq.dfs(y,rt,c,1);
}
fhq.dfs(rt,n,0,0);
for (int i=1;i<=m;i++)
printf("%d ",fhq.cnt[i]);
return 0;
}