codeforces 1249 D2 Too Many Segments (hard version) 贪心+树状数组

题意

给定n个线段,线段可以相交,第(i)个线段覆盖的区间为([l_i,r_i]),问最少删除多少个线段让覆盖每个点的线段数量小于等于k。

分析

从左往右扫每个点(x),若覆盖点(x)的线段数cnt大于k,则贪心的删去覆盖点(x)的线段中(r_i)(cnt-k)大的线段,因为点(x)左边的点的被覆盖数一定已经小于等于k了,删去(r_i)越大的线段越优。可以用个堆来维护覆盖点(x)的线段,用树状数组维护覆盖每个点的线段数量。

Code

#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define lson l,mid,p<<1
#define rson mid+1,r,p<<1|1
#define ll long long
using namespace std;
const int inf=1e9;
const int mod=1e9+7;
const int maxn=2e5+10;
int n,k;
int b[maxn];
void add(int x,int k){
	while(x<maxn) b[x]+=k,x+=x&-x; 
}
int dor(int x){
	int ret=0;
	while(x) ret+=b[x],x-=x&-x;
	return ret;
}
struct ppo{
	int l,r,i;
	bool operator <(const ppo &o) const{
		if(r==o.r&&l==o.l) return i<o.i;
		if(r==o.r) return l<o.l;
		return r<o.r;
	}
};
int ans[maxn];
struct node{
	int x,i;
};
vector<node>a[maxn];
int main(){
	ios::sync_with_stdio(false);
	//freopen("in","r",stdin);
	cin>>n>>k;
	for(int i=1,l,r;i<=n;i++){
		cin>>l>>r;
		a[l].pb(node{r,i});
		add(l,1);add(r+1,-1);
	}
	int tot=0;
	set<ppo>q;
	for(int i=1;i<maxn;i++){
		for(node x:a[i]) q.insert(ppo{i,x.x,x.i});
		int cnt=dor(i);
		while(cnt>k){
			auto it=q.end();
			--it;
			add(it->l,-1);add(it->r+1,1);
			ans[++tot]=it->i;
			q.erase(it);
			cnt--;
		}
	}
	cout<<tot<<'
';
	for(int i=1;i<=tot;i++){
		cout<<ans[i]<<" ";
	}cout<<'
';
	return 0;
}
原文地址:https://www.cnblogs.com/xyq0220/p/11731632.html