[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;
}
夜畔流离回,暗叹永无殿。
独隐万花翠,空寂亦难迁。
千秋孰能为,明灭常久见。
但得心未碎,踏遍九重天。