考虑所有$c=1$时,就是个经典的贪心:按右端点排序, 如果当前考虑的线段之内有点则跳过,否则选择最右边的点.
对于一般情况,设$c_i-$线段中已有的点数量 为$k$.
若$k<0$则跳过,否则选择最靠右的$k$个未被选择的点.
直接做时间复杂度为$\mathcal O(nV),V$为值域.
考虑数据结构优化.对于位置$pos$,有点记为1,无点记为0.因此”线段中已有的点数量”等价于一个区间和问题.
考虑”选择最靠右的$k$个未被选择的点.”.寻找某个常数的第$x$个位置是线段树/平衡树 上二分的经典问题.因此每次找当前区间右端点最左侧的0,并暴力单点修改即可,时间复杂度是$\mathcal O(n\log n+V\log V)$
//告别时间复杂度玄学的判负环,从我做起
/**********/
#define MAXN 50011
struct Segment_Tree
{
ll t[MAXN<<2|1];
#define rt t[num]
#define tl t[num<<1]
#define tr t[num<<1|1]
ll Qsum(un ql,un qr,un l=1,un r=MAXN-1,un num=1)
{
if(ql<=l&&r<=qr)return rt;
un mid=(l+r)>>1;ll ans=0;
if(ql<=mid)ans+=Qsum(ql,qr,l,mid,num<<1);
if(qr>mid)ans+=Qsum(ql,qr,mid+1,r,num<<1|1);
return ans;
}
void modify(un pos,ll k,un l=1,un r=MAXN-1,un num=1)
{
if(l==r){rt+=k;return;}
un mid=(l+r)>>1;
if(pos<=mid)modify(pos,k,l,mid,num<<1);
else modify(pos,k,mid+1,r,num<<1|1);
rt=tl+tr;
}
ll Qkth(ll k,un l=1,un r=MAXN-1,un num=1)//Query k-th 0
{
if(l==r)return l;
un mid=(l+r)>>1;
ll fl=mid-l+1-tl;
if(k<=fl)return Qkth(k,l,mid,num<<1);
else return Qkth(k-fl,mid+1,r,num<<1|1);
}
}sgt;
struct Seg
{
ll l,r,c;
bool operator <(const Seg& t)const{return r<t.r;}
}a[MAXN];
int main()
{
ll n=read(),ans=0;
for(ll i=1;i<=n;++i)a[i].l=read()+1,a[i].r=read()+1,a[i].c=read();
std::sort(a+1,a+n+1);
for(ll i=1;i<=n;++i)
{
ll k=a[i].c-sgt.Qsum(a[i].l,a[i].r);
if(k<0)continue;
ans+=k;
while(k--)
{
ll pos=sgt.Qkth(a[i].r-sgt.Qsum(1,a[i].r));
sgt.modify(pos,1);
}
}
printf("%lld",ans);
return 0;
}
orz
同样思路,
# 从后往前贪心放数即可。
对于这个可以用链表链接0(没选的元素) 可以保证O(1)放数
不过数据没有卡这个;
流弊
这个思路强啊👍
Orz wh
stO 达逼cat