loading

P7219 [JOISC 2020] 星座 3

题意

有一个大小为 \(n\times n\) 的网格,有 \(n\)\(a_i\),代表第 \(i\)\(a_i\sim n\) 行这些格子被禁掉了。还有 \(m\) 个点,每个点有权值 \(c_i\),你需要选出一些点删掉,代价是删掉的点的 \(c_i\) 之和,使得对于所有不包含被禁掉的格子的矩形内只有至多一个点。

\(n\le 2\times10^5\)

无脑做法

显然大矩形的限制严格强于被包含的小矩形,而大矩形的枚举就考虑枚举笛卡尔树上每个节点。设 \(f_{i,j}\) 表示笛卡尔树的 \(i\) 号节点,当前节点代表的矩形内部没选出点或选出了 \(j\) 号点的最小代价。如果将点按照先 \(x\)\(y\) 坐标排序的话,那么笛卡尔树子树向父亲转移时有些 \(j\) 会因为点的横坐标过小而转移到 0,这些 \(j\) 由此构成一个区间,可以进行线段树操作,所以可以线段树合并优化。复杂度一个 log。可能有若干瑕疵。

有脑做法

考虑贪心,从下往上考虑,遇到一个点,我们要么选择把这个点删了,要么选择将这个点支配的所有点删了,这里定义一个点支配另一个点为另一个点在前一个点上方,且存在一个矩形同时包含这两个点。继续往上的话,可能出现把该点留下而先前那点删掉会更优的情况,再考虑反悔贪心,只考虑横坐标在它以下的点,每个纵坐标记录如果在这个纵坐标下反悔需要付出多大的代价,如果反悔代价小于删除代价那么就反悔。这就是一个区间加、单点查,可以用树状数组维护。至于维护支配区间,只需要写一个 set 维护前驱后继即可。复杂度一个 log。

有脑做法的 code:

int n,m,a[maxn];
struct BIT{
	int c[maxn];
	void add(int x,int y){while(x<=n)c[x]+=y,x+=lowbit(x);}
	void upd(int l,int r,int x){add(l,x),add(r+1,-x);}
	int qry(int x){int res=0;while(x)res+=c[x],x-=lowbit(x);return res;}
} T;
vector<pii>vec[maxn];
vector<int>aec[maxn];
inline void solve_the_problem(){
	n=rd();
	rep(i,1,n)a[i]=rd(),aec[a[i]+1].pb(i);
	m=rd();
	rep(i,1,m){
		int x=rd(),y=rd(),z=rd();
		vec[y].pb(mp(x,z));
	}
	int ans=0;
	set<int>s;
	rep(i,0,n+1)s.insert(i);
	rep(i,1,n){
		for(int u:aec[i])s.erase(u);
		for(pii _:vec[i]){
			int u=_.fi,val=_.se;
			auto it=s.upper_bound(u);
			int l=*prev(it)+1,r=*it-1;
			int fval=T.qry(u);
			if(val<fval)ans+=val;
			else ans+=fval,T.upd(l,r,val-fval);
		}
	}
	write(ans);
}
posted @ 2025-07-12 11:13  dcytrl  阅读(35)  评论(2)    收藏  举报