[CF702F] T-Shirts

\(\text{Problem}:\)T-Shirts

\(\text{Solution}:\)

在序列操作的问题中,一种常规的思想是使得被操作物品的信息尽可能少。故转化题目为:将物品以 \(q_{i}\) 从大到小为第一关键字,\(c_{i}\) 从小到大为第二关键字排序。枚举物品 \(i\),对于每个人 \(j\),若 \(v_{j}\geq c_{i}\),则 \(v_{j}\) 减去 \(c_{i}\),\(g_{j}\) 加 \(1\);否则不进行操作。最后查询 \(\forall i\in [1,m],g_{i}\)。

考虑用平衡树维护有序的 \(v_{i}\)。将大于等于 \(c_{i}\) 的 \(v_{j}\) 都打上区间减的 \(tag\) 是不可行的,因为会破坏平衡树的结构。现在考虑这样一种做法:

  • \(0\leq v_{j}<c_{i}\),不操作。

  • \(c_{i}\leq v_{i}<2c_{i}\),将其减去 \(c_{i}\) 后暴力插入平衡树中。

  • \(v_{i}\geq 2c_{i}\),打上区间减 \(c_{i}\) 的标记。

答案正确性显然。分析第二种操作的时间复杂度。发现对于每个 \(v_{j}\),至多只会进行 \(\log c_{i}\) 次操作二,故复杂度正确。

总时间复杂度 \(O(n\log^2 n)\)(令 \(c_{i}\) 与 \(n\) 同阶)。

\(\text{Code}:\)

#include <bits/stdc++.h>
#pragma GCC optimize(3)
//#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
#define vi vector<int>
#define vpi vector<pair<int,int>>
using namespace std; const int N=200010;
inline int read()
{
	int s=0, w=1; ri char ch=getchar();
	while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); }
	while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar();
	return s*w;
}
int n,m;
struct Node { int c,q; }a[N]; int b[N];
inline bool cp(Node x,Node y) { return x.q==y.q?x.c<y.c:x.q>y.q; }
int root,ch[N][2],siz[N],rnd[N],val[N],sum[N],t1[N],t2[N];
inline int New_Node(int x,int k) { val[k]=x, siz[k]=1, rnd[k]=rand(); return k; }
inline void Push_Up(int x) { siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1; }
inline void Push_Down(int x)
{
	if(ch[x][0]) t1[ch[x][0]]+=t1[x], t2[ch[x][0]]+=t2[x], val[ch[x][0]]-=t1[x], sum[ch[x][0]]+=t2[x];
	if(ch[x][1]) t1[ch[x][1]]+=t1[x], t2[ch[x][1]]+=t2[x], val[ch[x][1]]-=t1[x], sum[ch[x][1]]+=t2[x];
	t1[x]=t2[x]=0;
}
void Split(int root,int k,int &x,int &y)
{
	if(!root) { x=y=0; return; }
	Push_Down(root);
	if(val[root]<=k) x=root, Split(ch[root][1],k,ch[root][1],y);
	else y=root, Split(ch[root][0],k,x,ch[root][0]);
	Push_Up(root);
}
int Merge(int x,int y)
{
	if(!x||!y) return x|y;
	Push_Down(x), Push_Down(y);
	if(rnd[x]<rnd[y]) { ch[x][1]=Merge(ch[x][1],y), Push_Up(x); return x; }
	else { ch[y][0]=Merge(x,ch[y][0]), Push_Up(y); return y; }
}
inline void Insert(int k,int ui)
{
	int x,y;
	Split(root,k,x,y);
	root=Merge(Merge(x,New_Node(k,ui)),y);
}
void Change(int x,int &to,int C)
{
	Push_Down(x);
	if(ch[x][0]) Change(ch[x][0],to,C);
	if(ch[x][1]) Change(ch[x][1],to,C);
	ch[x][0]=ch[x][1]=0;
	int y,z;
	val[x]-=C, sum[x]++;
	Split(to,val[x],y,z);
	to=Merge(Merge(y,x),z);
}
inline void UpDate(int C)
{
	int x,y,z;
	Split(root,C-1,x,y);
	Split(y,C*2-1,y,z);
	if(z) val[z]-=C, t1[z]+=C, sum[z]++, t2[z]++;
	Change(y,x,C);
	root=Merge(x,z);
}
void Down(int x)
{
	Push_Down(x);
	if(ch[x][0]) Down(ch[x][0]);
	if(ch[x][1]) Down(ch[x][1]);
}
signed main()
{
	srand(time(NULL));
	n=read();
	for(ri int i=1;i<=n;i++) a[i].c=read(), a[i].q=read();
	sort(a+1,a+1+n,cp);
	m=read();
	for(ri int i=1;i<=m;i++) b[i]=read(), Insert(b[i],i);
	for(ri int i=1;i<=n;i++) UpDate(a[i].c);
	Down(root);
	for(ri int i=1;i<=m;i++) printf("%d ",sum[i]);
	puts("");
	return 0;
}
上一篇:LeetCode(26)连续的子数组和(中等)


下一篇:jdbc 占位符插入null值 NullPointerException