CF1842E Tenzing and Triangle 题解
题意
- 现在在平面直角坐标系内有
n 个点,每个点有一个权值w_i ,对于所有点(x_i,y_i) ,有0 \le x_i,y_i,x_i+y_i < k 。 - 现在丁真想要删除这个平面上的所有点,有两种删点的方法:
- 直接删掉某个点,代价为该点的权值
w_i 。 - 任意选择一个点
(x_0,y_0) (不一定在原来的点集中),需要保证0 \le x_0,y_0,x_0+y_0 < k ,删除所有满足x_i \ge x_0 且y_i\ge y_0 的点,即删除这个点与函数y=-x+k 的图像围成的等腰直角三角形中所有点。代价为三角形直角边长l\times A 。
- 直接删掉某个点,代价为该点的权值
- 两个操作可以执行任意次,求删除所有点的最小代价。
题解
这道题赛时想了个贪心伪掉了。
首先容易证明假如有两个三角形重合了必定不优:
在左图中,原本两个三角形只覆盖了绿色区域,不仅没有覆盖到黄色区域,代价还比红色三角形大,故两个黑色三角形必定不优,另外像右图一样只有一点重合的也不优,证明同理。
然后还有一个转化,假设所有点都用操作一删除,然后每次一个对
容易想到 dp,设
假设有一个操作二
由于重合了不优所以要从
发现
这里
然后我们把和
对于一个特定的
现在难点就是对
假设有一个点坐标为
那么就可以用一棵线段树维护
code
//author: six-floor-slip-liu
#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define forup(i,s,e) for(i64 i=(s);i<=(e);i++)
#define fordown(i,s,e) for(i64 i=(s);i>=(e);i--)
using namespace std;
using i64=long long;
#define gc getchar()
inline i64 read(){
i64 x=0,f=1;char c;
while(!isdigit(c=gc)) if(c=='-') f=-1;
while(isdigit(c)){x=(x<<3)+(x<<1)+(c^48);c=gc;}
return x*f;
}
#undef gc
const i64 N=2e5+5,inf=1e18;
i64 n,k,a,ans;
struct Node{
i64 x,y,cst;
}s[N];
bool cmp(Node a,Node b){
if(a.x!=b.x) return a.x>b.x;
return a.y>b.y;
}
i64 dp[N];
struct SegmentTree{
#define mid ((l+r)>>1)
#define lson l,mid,id<<1
#define rson mid+1,r,id<<1|1
i64 querymin[N<<2],mark[N<<2];
void PushUp(i64 id){//回溯
querymin[id]=min(querymin[id<<1],querymin[id<<1|1]);
}
void PushDown(i64 id){//下传懒标记
querymin[id<<1|1]+=mark[id];
querymin[id<<1]+=mark[id];
mark[id<<1|1]+=mark[id];
mark[id<<1]+=mark[id];
mark[id]=0;
}
void QueryAdd(i64 L,i64 R,i64 X,i64 l=0,i64 r=k+1,i64 id=1){
//区间加
if(L<=l&&r<=R){
querymin[id]+=X;
mark[id]+=X;
return;
}
if(mark[id]&&l<r) PushDown(id);
if(L<=mid) QueryAdd(L,R,X,lson);
if(mid< R) QueryAdd(L,R,X,rson);
PushUp(id);
}
i64 AskMin(i64 L,i64 R,i64 l=0,i64 r=k+1,i64 id=1){
//区间取最小值
if(L<=l&&r<=R){
return querymin[id];
}
if(mark[id]&&l<r) PushDown(id);
i64 res=inf;
if(L<=mid) res=min(res,AskMin(L,R,lson));
if(mid< R) res=min(res,AskMin(L,R,rson));
PushUp(id);
return res;
}
}mt;
signed main(){
n=read();k=read();a=read();
forup(i,1,n){
s[i].x=read();s[i].y=read();s[i].cst=read();
ans+=s[i].cst;
}
sort(s+1,s+n+1,cmp);
i64 l=1;
mt.QueryAdd(k+1,k+1,k*a);//先上传边界
fordown(i,k,0){
while(s[l].x==i){//把所有 x=i 的全部上传
mt.QueryAdd(k-s[l].y+1,k+1,-s[l].cst);
++l;
}
dp[i]=min(0ll,mt.AskMin(i+1,k+1)-i*a);
mt.QueryAdd(i,i,dp[i]+(i-1)*a);
//单点上传 dp[j]+(j-1)*A,s 放到后面处理。
}
printf("%lld",ans+dp[0]);//注意加上权值总和。
}