【CF702F】T-Shirts

题目

题目链接: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;
} 
上一篇:CAA获取Container——获取其他工厂


下一篇:Android通过rc文件控制GPIO口启动车载功放