题解:P16725 [GKS 2019 #A] Contention
lailai0916 · · 题解
题意简述
有
求一种处理顺序,使所有订单所得座位数的最小值尽可能大,输出这个最大值。
解题思路
从最后处理的订单开始考虑。在当前剩余订单中,记
若将
下面证明这个贪心。固定目标下界
把我们选择的订单
于是,只要存在最小分配数至少为
删除订单后,其他订单的独占座位数只增不减。一个座位对剩余订单产生新的贡献,仅发生在它的覆盖次数从
覆盖次数只会下降,每个座位至多被这样处理一次。我们不必在每次删除后重新统计所有订单。
先将所有
每段维护两个量:覆盖次数,以及所有覆盖订单编号的异或和。删除订单
线段树维护尚未计入贡献的段的最小覆盖次数,并支持区间减一和区间异或。若某个节点的最小值为
已经处理过的叶子将最小值改为足够大的哨兵值,今后不再报告;初始没有被任何订单覆盖的叶子也直接屏蔽。后续至多执行
用最大堆保存订单的当前独占数。独占数增加时插入一个新条目,不必删除旧条目;取堆顶时,丢弃已删除订单的条目,以及数值不等于当前
一次删除后的收集过程中,所有未屏蔽段的覆盖次数仍至少为
每个离散段最多收集一次,总共只有
参考代码
#include <bits/stdc++.h>
using namespace std;
using pii=pair<int,int>;
const int N=60005;
const int inf=0x3f3f3f3f;
int a[N],L[N],R[N],s[N],b[N],cnt[N];
bool vis[N];
priority_queue<pii> q;
struct SEG
{
int val[N<<2],sum[N<<2],tag[N<<2],xr[N<<2];
void push_up(int u)
{
val[u]=min(val[u<<1],val[u<<1|1]);
}
void gx(int u,int x,int y)
{
val[u]+=x;
tag[u]+=x;
sum[u]^=y;
xr[u]^=y;
}
void push_down(int u)
{
gx(u<<1,tag[u],xr[u]);
gx(u<<1|1,tag[u],xr[u]);
tag[u]=xr[u]=0;
}
void build(int u,int l,int r)
{
tag[u]=xr[u]=sum[u]=0;
if(l==r)
{
val[u]=s[l]?s[l]:inf;
sum[u]=b[l];
return;
}
int mid=(l+r)>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
push_up(u);
}
void update(int u,int l,int r,int x,int y,int k)
{
if(x<=l&&r<=y){gx(u,-1,k);return;}
push_down(u);
int mid=(l+r)>>1;
if(x<=mid)update(u<<1,l,mid,x,y,k);
if(y>mid)update(u<<1|1,mid+1,r,x,y,k);
push_up(u);
}
void query(int u,int l,int r)
{
if(val[u]!=1)return;
if(l==r)
{
int id=sum[u];
cnt[id]+=a[l+1]-a[l];
q.push({cnt[id],id});
val[u]=inf;
return;
}
push_down(u);
int mid=(l+r)>>1;
query(u<<1,l,mid);
query(u<<1|1,mid+1,r);
push_up(u);
}
}seg;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin>>T;
int _=0;
while(T--)
{
_++;
int n,m;
cin>>n>>m;
for(int i=1;i<=m;i++)
{
cin>>L[i]>>R[i];
R[i]++;
a[i*2-1]=L[i];
a[i*2]=R[i];
cnt[i]=vis[i]=0;
}
sort(a+1,a+m*2+1);
int len=unique(a+1,a+m*2+1)-a-1;
fill(s,s+len+1,0);
fill(b,b+len+1,0);
q=priority_queue<pii>();
for(int i=1;i<=m;i++)
{
L[i]=lower_bound(a+1,a+len+1,L[i])-a;
R[i]=lower_bound(a+1,a+len+1,R[i])-a;
s[L[i]]++;
s[R[i]]--;
b[L[i]]^=i;
b[R[i]]^=i;
q.push({0,i});
}
for(int i=1;i<len;i++)s[i]+=s[i-1],b[i]^=b[i-1];
seg.build(1,1,len-1);
seg.query(1,1,len-1);
int ans=n;
for(int i=1;i<=m;i++)
{
while(vis[q.top().second]||q.top().first!=cnt[q.top().second])q.pop();
int u=q.top().second;
q.pop();
ans=min(ans,cnt[u]);
vis[u]=1;
seg.update(1,1,len-1,L[u],R[u]-1,u);
seg.query(1,1,len-1);
}
cout<<"Case #"<<_<<": "<<ans<<'\n';
}
return 0;
}