[JOISC 2020 Day4T3] 治疗方案

题目链接

首先我们看到题,发现不会,考虑上网搜题解,然后会发现这是一道dp.

首先这是看出一道dp,先思考(n^2)暴力怎么做,显然我们每个操作必须"连起来"。这样才能确保所有人都被治愈。令 (dp[i]) 表示治疗到 (r[i]) 所需要的最小花费。

我们可以得出 (dp[i] = min_{j = 1}^{m}dp[j] + c[i]), 对于所有 (j) 满足 (r[i] le l[j] + |T[i] - T[j]| + 1), 且 (j ot= i)。这个东西的时间复杂度是 (n^2) 的。

我们发现拆了绝对值之后,每个 (i) 对应的能转移的 (j) 是原序列按 (T) 排序后,对应的一段区间。然后又能发现,这个dp可以想dij一样,每次取最小的更新。

那么我们把它们丢到两棵(对应着拆绝对值)线段树上,然后跑一个很像最短路的东西就行了。

我的写法时间复杂度是 (mlog^2m) , 被wasa855神仙的 (mlogm) 方法爆踩。 说明我太菜了/kk

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
#define N 100005
#define pll pair<ll, ll>

int n, m;

struct qus
{
	int T, l, r;
	ll c;

	bool operator < (const qus &o) const
	{
		return T < o.T;
	}
}a[N];

ll dis[N];

priority_queue <pll> q;

bool vis[N], inq[N];

struct Seg
{
	set <pll> s[N << 2];

	void insert(int rt, int l, int r, int x, pll v)
	{
		s[rt].insert(v);
		if(l == r) return;
		int mid = l + r >> 1;
		if(x <= mid) insert(rt << 1, l, mid, x, v);
		else insert(rt << 1 | 1, mid + 1, r, x, v);
	}

	void update(int rt, int l, int r, int x, int y, int v, ll p)
	{
		// cout << l << ' ' << r << ' ' << x << ' ' << y << endl;
		if(l == x && r == y)
		{
			for(; !s[rt].empty() && s[rt].begin()->first <= v;)
			{
				int u = s[rt].begin()->second;
				if(!inq[u])
				{
					inq[u] = 1;
					dis[u] = p + a[u].c;
					q.push(make_pair(-dis[u], u));
				}
				s[rt].erase(s[rt].begin());
			}
			return;
		}
		int mid = l + r >> 1;
		if(y <= mid) update(rt << 1, l, mid, x, y, v, p);
		else if(x > mid) update(rt << 1 | 1, mid + 1, r, x, y, v, p);
		else update(rt << 1, l, mid, x, mid, v, p), update(rt << 1 | 1, mid + 1, r, mid + 1, y, v, p);
	}
}tr1, tr2;

int main()
{
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= m; i++)
	{
		scanf("%d%d%d%d", &a[i].T, &a[i].l, &a[i].r, &a[i].c);
	}
	sort(a + 1, a + m + 1);
	for(int i = 1; i <= m; i++) tr1.insert(1, 1, m, i, make_pair(a[i].l - a[i].T, i));
	for(int i = 1; i <= m; i++) tr2.insert(1, 1, m, i, make_pair(a[i].l + a[i].T, i));
	for(int i = 1; i <= m; i++) dis[i] = (a[i].l == 1) ? a[i].c : (1ll << 60);
	for(int i = 1; i <= m; i++) if(a[i].l == 1) inq[i] = 1, q.push(make_pair(-a[i].c, i));
	while(!q.empty())
	{
		int u = q.top().second; q.pop();
		// cout << u << endl;
		if(vis[u]) continue;
		vis[u] = 1;
		if(u != 1) tr1.update(1, 1, m, 1, u - 1, a[u].r - a[u].T + 1, dis[u]);
		if(u != m) tr2.update(1, 1, m, u + 1, m, a[u].r + a[u].T + 1, dis[u]);
	}
	ll ans = (1ll << 60);
	for(int i = 1; i <= m; i++) 
	{
		if(a[i].r == n)
		{
			ans = min(ans, dis[i]);
		}
	}
	if(ans != (1ll << 60)) printf("%lld
", ans);
	else printf("-1
");
	return 0;
}
原文地址:https://www.cnblogs.com/LJB00131/p/12582379.html