[2021.8集训Day2/JZOJ.1254]清理牛棚


[2021.8集训Day2/JZOJ.1254]清理牛棚

题目

思路

当每一个 S S S都比较大时,总费用可能超过 inf ⁡ \inf inf,但是数据较水,这个问题可以自行解决.

线段树优化DP

T4竟是最水的一道题?!

f i f_i fi表示前 i i i秒都有奶牛打扫的最小代价.显然,对于每一头奶牛,有
f t 2 = min ⁡ ( f t 2 , s + min ⁡ i = t 1 − 1 t 2 − 1 f i ) f_{t_2}=\min(f_{t_2},s+\min^{t_2-1}_{i=t_1-1}f_i) ft2=min(ft2,s+i=t11mint21fi)
对于后面的取最小值,明显可以线段树优化.

把每一头奶牛按 t 2 t_2 t2​递增排序再枚举即可.

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 10010 , M = 90000;
const int INF = 0x3fffffff;

int read() {
	int re = 0;
	char c = getchar();
	bool negt = false;
	while(c < '0' || c > '9')
		negt |= (c == '-') , c = getchar();
	while(c >= '0' && c <= '9')
		re = (re << 1) + (re << 3) + c - '0' , c = getchar();
	return negt ? -re : re;
}
class SegmentTreee {
	private :
		struct TreeNode {
			int l , r , ls , rs , dat;
		}node[M * 4];
		int cnt;
	public :
		inline int newnode () {
			return ++cnt;
		}
		int build(int l , int r) {
			int id = newnode();
			node[id].l = l , node[id].r = r , node[id].dat = INF;
			if(l == r)
				return id;
			int mid = (l + r) / 2;
			node[id].ls = build(l , mid);
			node[id].rs = build(mid + 1 , r);
			return id;
		}
		void change(int p , int pos , int dat) {
			if(node[p].l == node[p].r) {
				node[p].dat = dat;
				return;
			}
				
			if(pos <= node[node[p].ls].r)
				change(node[p].ls , pos , dat);
			else
				change(node[p].rs , pos , dat);
			node[p].dat = min(node[node[p].ls].dat , node[node[p].rs].dat);
		}
		int query(int p , int l , int r) {
			if(l <= node[p].l && r >= node[p].r)
				return node[p].dat;
			if(l > node[p].r || r < node[p].l)
				return INF;
			return min(query(node[p].ls , l , r) , query(node[p].rs , l , r));
		}
}segT;
struct COW {
	int st , end , cos;
}cow[N];
bool cmp(COW a , COW b) {
	return a.end < b.end;
}

int n , m;
int root;

int cos[M];
int main() {
	n = read();
	int st = read() , end = read() , delta = st - 1;//需要打扫的时间:[1,m]
	m = end - delta;
	for(int i = 1 ; i <= n ; i++)
		cow[i].st = read() - delta , cow[i].end = read() - delta , cow[i].cos = read();
		
	sort(cow + 1 , cow + n + 1 , cmp);
	
	root = segT.build(0 , m);
	
	segT.change(root , 0 , 0);
	
	memset(cos , 0x3f , sizeof(cos));
	for(int i = 1 ; i <= n ; i++) {
		int tmp = cow[i].cos + segT.query(root , cow[i].st - 1 , cow[i].end - 1);
		if(tmp < cos[cow[i].end]) {
			cos[cow[i].end] = tmp;
			segT.change(root , cow[i].end , tmp);
		}
	}
	cout << (cos[m] == 1061109567 ? -1 : cos[m]);
	return 0;
}

最短路

**(这里的边都是有向边)**可以考虑建模,每一头奶牛 t 1 , t 2 , s t_1,t_2,s t1,t2,s​从 t 1 − 1 t_1-1 t11​到 t 2 t_2 t2​连一条边权为 s s s​的边,对于 E − 1 E-1 E1​到 M − 1 M-1 M1​的每一秒 i i i,​从 i + 1 i+1 i+1​到 i i i​连一条边权为 0 0 0​的边.从 E − 1 E-1 E1​出发, M M M​结束,查询最短路即可

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>

using namespace std;

const int N = 10010 , M = 90000;
const int INF = 0x3fffffff;

int read() {
	int re = 0;
	char c = getchar();
	bool negt = false;
	while(c < '0' || c > '9')
		negt |= (c == '-') , c = getchar();
	while(c >= '0' && c <= '9')
		re = (re << 1) + (re << 3) + c - '0' , c = getchar();
	return negt ? -re : re;
}
struct EDGE {
	int to , nxt , val;
}ed[M + N];
int head[M];
void addedge(int u , int v , int val) {
	static int cnt = 0;
	++cnt;
	ed[cnt].to = v , ed[cnt].val = val , ed[cnt].nxt = head[u] , head[u] = cnt;
}

int n , m;

bool inq[M];
int dis[M];
void spfa() {//懒得写dij,勿喷SPFA QAQ
	memset(dis , 0x3f , sizeof(dis));
	dis[0] = 0;
	
	queue <int> q;
	inq[0] = true , q.push(0);
	while(!q.empty()) {
		int u = q.front();
		inq[u] = false , q.pop();
		for(int i = head[u] ; i ; i = ed[i].nxt) {
			int v = ed[i].to;
			if(dis[v] > dis[u] + ed[i].val) {
				dis[v] = dis[u] + ed[i].val;
				if(!inq[v])
					inq[v] = true , q.push(v);
			}
		}
	}
} 
int main() {
	n = read();
	int st = read() , end = read() , delta = st - 1;
	m = end - delta;
	
	for(int i = 0 ; i < m ; i++)
		addedge(i + 1  , i , 0);
	for(int i = 1 ; i <= n ; i++) {
		int u = read() - delta - 1 , v = read() - delta , val = read();
		addedge(u , v , val);
	}
	
	spfa();
	cout <<  (dis[m] == 1061109567 ? -1 : dis[m]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值