一、题目
点此看题
题目描述
有n种T恤,每种有价格ci和品质qi。有m个人要买T恤,第i个人有vi元,每人每次都会买一件能买得起的qi最大的T恤。一个人只能买一种T恤一件,所有人之间都是独立的。问最后每个人买了多少件T恤?如果有多个qi最大的T恤,会从价格低的开始买。
数据范围
1
≤
n
,
m
≤
2
e
5
1\leq n,m\leq 2e5
1≤n,m≤2e5,
1
≤
c
,
q
,
v
≤
1
0
9
1\leq c,q,v\leq 10^9
1≤c,q,v≤109
二、解法
0x01 暴力
一个很显然的做法就是先把每件T-shirt以品质为第一关键字,价格为第二关键字排序,对于每一个人的询问在线性扫一遍,能买则买,这种做法不可优化。
0x02 另一种思考
与其去看人能买那些衣服,不如看衣服能被那些人买,我们还是先排序,每次看那些人能买到这种衣服,时间复杂度虽然不降,但是好像能够用 treap \text{treap} treap优化,我们维护一个关于人的平衡树,每次对钱数不小于 c c c的区间修改,但是要考虑一个问题,修改之后不能直接 merge \text{merge} merge,因为不能保证修改后区间的钱数整体不小于另一个区间,那我们就把 c ≤ v < 2 c c\leq v< 2c c≤v<2c的区间暴力插入,其他的区间插入。
这样的复杂度好像还是会超,但仔细想想,这些被暴力插入的人的钱数至少减少了一半,也就是他们的钱数是以 log \log log的速度下降的,每个人最多被单独插入 log v \log v logv次,这样时间复杂度就有了保证。
0x03 tips and code
1、最后还要整体下传一次标记。
2、单个插入不要一个一个分裂,把区间分裂出来在一个一个插入(不然会
T
T
T)。
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <ctime>
#include <queue>
using namespace std;
const int MAXN = 200005;
int read()
{
int x=0,flag=1;
char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,m,rt;
int ch[MAXN][2],siz[MAXN],ans[MAXN],val[MAXN],hp[MAXN],la[MAXN],ad[MAXN];
struct data
{
int c,q;
bool operator < (const data &R) const
{
if(q==R.q) return c<R.c;
return q>R.q;
}
} s[MAXN];
struct node
{
int p[2];
node()
{
p[0]=p[1]=0;
}
} emp;
void up(int x)
{
siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;
}
void down(int x)
{
if(la[x])
{
la[ch[x][0]]+=la[x];
la[ch[x][1]]+=la[x];
val[ch[x][0]]-=la[x];
val[ch[x][1]]-=la[x];
la[x]=0;
}
if(ad[x])
{
ad[ch[x][0]]+=ad[x];
ad[ch[x][1]]+=ad[x];
ans[ch[x][0]]+=ad[x];
ans[ch[x][1]]+=ad[x];
ad[x]=0;
}
}
node split(int x,int v)
{
if(!x) return emp;
down(x);
int d=val[x]<=v;
node y=split(ch[x][d],v);
ch[x][d]=y.p[d^1];
y.p[d^1]=x;
up(x);
return y;
}
node split_(int x,int s)
{
if(!x) return emp;
down(x);
node y;
if(siz[ch[x][0]]>=s)
{
y=split_(ch[x][0],s);
ch[x][0]=y.p[1];
y.p[1]=x;
}
else
{
y=split_(ch[x][1],s-siz[ch[x][0]]-1);
ch[x][1]=y.p[0];
y.p[0]=x;
}
up(x);
return y;
}
int merge(int x,int y)
{
if(!x || !y) return x+y;
if(hp[x]<hp[y])
{
down(x);
ch[x][1]=merge(ch[x][1],y);
up(x);
return x;
}
down(y);
ch[y][0]=merge(x,ch[y][0]);
up(y);
return y;
}
void ins(int &rt,int i,int v)
{
node x=split(rt,v-1);
ch[i][0]=ch[i][1]=0;
siz[i]=1;
hp[i]=rand();
val[i]=v;
rt=merge(x.p[0],merge(i,x.p[1]));
}
int pre(int &rt,int v)
{
node x=split(rt,v-1),y=split_(x.p[0],siz[x.p[0]]-1);
int ans=val[y.p[1]];
rt=merge(merge(y.p[0],y.p[1]),x.p[1]);
return ans;
}
void print(int x)
{
if(!x) return ;
down(x);
print(ch[x][0]);
//printf("%d ",val[x]);
print(ch[x][1]);
}
int main()
{
//srand(time(0));
n=read();
for(int i=1; i<=n; i++)
{
s[i].c=read();
s[i].q=read();
}
sort(s+1,s+1+n);
m=read();
for(int i=1; i<=m; i++)
ins(rt,i,read());
for(int i=1; i<=n; i++)
{
node x=split(rt,s[i].c-1);
la[x.p[1]]+=s[i].c;
val[x.p[1]]-=s[i].c;
ad[x.p[1]]++;
ans[x.p[1]]++;
node y=split(x.p[1],2*s[i].c-1);
queue<int> q;
q.push(y.p[0]);
while(!q.empty())
{
int t=q.front();
q.pop();
down(t);
if(ch[t][0]) q.push(ch[t][0]);
if(ch[t][1]) q.push(ch[t][1]);
ins(x.p[0],t,val[t]);
}
rt=merge(x.p[0],y.p[1]);
}
print(rt);
for(int i=1; i<=m; i++)
printf("%d ",ans[i]);
}