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);
}

浙公网安备 33010602011771号