CF1842E Tenzing and Triangle - 线段树优化 dp -

题目链接:https://codeforces.com/contest/1842/problem/E

题解:
首先,如果两个等腰三角形相交了,那答案肯定不会更优。因此不会相交。
先考虑一个 n2 的 dp:
dpi 表示考虑到 x=i 时的最小代价,首先可以先都加一个 ci,这样只需要考虑三角形覆盖范围内的 ci 减去即可。
dpidpjcost(j+1,i)+A×(ij),其中 cost(j+1,i) 表示这两个坐标所确定的等腰直角三角形内部的 ci 之和,减是因为最后要加一个 ci
转移的时候,可以每次更新一下 jcost

如何优化?首先,为了方便,令 y:=ky,具体是将后缀和转化为前缀和,详细见后。这样,覆盖所需要的等腰三角形就变成了

修改一下 dp 的定义:令 dpi 表示考虑覆盖到 y=i 时的最小代价
考虑一下一个点 (x,i) 会对哪些 dp 值产生贡献?显然是 1..x(也就是说,当 i1..x 转移过来时,cost 都要加上 (x,i) 点的贡献)
理解的话可以回到 y 的含义没有变的时候image
这就是一个前缀加。利用线段树维护一下 dpii×A,然后就是前缀和,区间 min。注意线段树下标要从 0 开始。
最后加上 ci 即可

// by SkyRainWind
#include <bits/stdc++.h>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define pii pair<int,int>
#define pb push_back

using namespace std;

typedef long long ll;
typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f, maxn = 2e5+5;

int n,k,coef;
vector<pii>p[maxn];
ll f[maxn];

struct segm{
	ll mn,lazy;
}se[maxn << 2];

void build(int l,int r,int num){
	se[num].mn = 1e18;
	se[num].lazy = 0;
	if(l == r){
		return ; 
	}
	int mid=l+r>>1;
	build(l,mid,num << 1);build(mid+1,r,num<<1|1);
}

void pushdown(int num,int l,int r){
	if(!se[num].lazy)return ;
	int mid=r-l+1;
	ll lz = se[num].lazy;
	se[num << 1].mn += lz, se[num << 1|1].mn += lz;
	se[num << 1].lazy += lz, se[num << 1|1].lazy += lz;
	se[num].lazy = 0;
}

void update(int l,int r,int x,ll to,int num){
	if(r <= x){
		se[num].mn += to;
		se[num].lazy += to;
		return ;
	}
	pushdown(num,l,r);
	int mid = l+r>>1;
	if(x <= mid)update(l,mid,x,to,num << 1);
	else update(l,mid,x,to,num << 1), update(mid+1,r,x,to,num << 1|1);
	se[num].mn = min(se[num << 1].mn, se[num << 1|1].mn);
}

void upd(int l,int r,int x,ll to,int num){
	if(l == r){
		se[num].mn = to;
		return ;
	}
	pushdown(num,l,r);
	int mid = l+r>>1;
	if(x <= mid)upd(l,mid,x,to,num << 1);
	else upd(mid+1,r,x,to,num << 1|1);
	se[num].mn = min(se[num << 1].mn, se[num << 1|1].mn);
}

ll query(int l,int r,int x,int y,int num){
	if(x <=l && r<=y)return se[num].mn;
	int mid = l+r>>1;
	pushdown(num,l,r);
	if(y<=mid)return query(l,mid,x,y,num<<1);
	else if(x>mid)return query(mid+1,r,x,y,num<<1|1);
	else return min(query(l,mid,x,y,num<<1),query(mid+1,r,x,y,num<<1|1));
}

signed main(){
	scanf("%d%d%d",&n,&k,&coef);
	ll bs = 0;
	for(int i=1;i<=n;i++){
		int x,y,c;scanf("%d%d%d",&x,&y,&c);
		y = k-y;
		p[y].pb(mpr(x, c));
		bs += c;
	}
	build(0,k,1);
	upd(0,k,0,0,1);
	for(int i=1;i<=k;i++){
		for(auto it : p[i]){
			int y = it.first;
			ll c = it.second;
			update(0,k,y,-c,1);
		}
		f[i] = min(query(0,k,0,i-1,1) + 1ll*i*coef, f[i-1]);
		upd(0,k,i,f[i]-1ll*i*coef,1);
	}
	printf("%lld\n",f[k] + bs);

	return 0;
}
posted @   SkyRainWind  阅读(50)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示
点击右上角即可分享
微信分享提示