首页 > 其他分享 >P2305 [NOI2014] 购票

P2305 [NOI2014] 购票

时间:2023-09-04 19:33:12浏览次数:39  
标签:const int mid 线段 购票 P2305 NOI2014 return id

P2305 [NOI2014] 购票

Solution

记 \(f_{i}\) 表示 \(i\) 节点处的答案。\(f_1 = 0\)。记 \(d_i\) 表示根节点到点 \(i\) 的距离,容易得到 \(O(n^2)\) 的 dp 转移:

\[f_{i} \xleftarrow{\min} f_j + (d_i - d_j)\times p_i + q_i, d_i - d_j \le l_i \]

设 \(y = f_i - d_i \times p_i - q_i, slope = -d_j, x = p_i, b = f_j\),这是一个斜率优化的形式。

但我们更应该注意,\(y = slope\times x + b\) 的一次函数形式可以用李超线段树维护,并且容易在树上实现撤销。

麻烦在于有 \(d_i - d_j \le l_i\) 的限制,那么需要实现任意一条链上的线段并的信息查询。

首先考虑序列上的区间查询,可以采用 线段树套李超线段树,外层线段树的叶节点存每个位置的一条线段,外层线段树上的一个节点将其子树内包含的所有线段并起来。这个过程并不用真的进行 pushup,而是对每一条线段直接插到外层线段树上的 \(\log\) 个节点(的李超线段树)上去。

然后考虑树上的区间查询。一种 naive 的想法是树链剖分,做到时间复杂度 \(O(n\log^3{n})\),空间复杂度 \(O(n\log{n})\)(考虑线段总数为 \(O(n)\),每条线段至多插入 \(O(\log{n})\) 次,由于李超线段树的特性——信息并不一定要保存在叶节点,因此遇到空节点就会立即 return,也就是说,插入一条线段最多导致动态开出 \(1\) 个点,所以外层线段树上所有节点的李超线段树的节点总数是 \(O(n\log{n})\) 的)。

优化:将外层线段树上的叶节点对应的下标定义为原树上节点的 出栈序(最后一次被遍历到的时间顺序)。在 dfs 的过程中,记 \(ord_i\) 表示点 \(i\) 的出栈序,若能更新一个点 \(u\) 的最远点为 \(v\),则外层线段树上区间查询 \(ord_u\) 到 \(ord_v\) 即可得到 \(u \to v\) 的链上所有线段并的结果。可以这样做的正确性保证在于,\(ord_u \sim ord_v\) 中对应两类点,一类是 \(u \to v\) 链上的点,另一类是不在链上的点,而后者此时一定未被遍历,信息未被上传,因此在 \(ord_u \sim ord_v\) 内的查询是十分精确的。

该做法的时间复杂度为 \(O(n\log^2{n})\),空间复杂度仍为 \(O(n\log{n})\)。

总结一下此题给我印象极深的两点:

  • “线段并” 这个一眼李超线段树合并的东西,通过外层套上一棵线段树,将多条线段的 “合并” 转化为每条线段的 “插入”

    在外层线段树上维护线段并,本质上与李超线段树合并擅长维护的 “子树信息合并到父节点” 的问题(如 CF932F Escape Through Leaf)本质相同。但在这里,初始时所有线段都处于叶节点处。在我们构建出的深度为 \(O(\log{n})\) 的外层线段树中,暴力将一条线段插入到包含它的所有节点中,时间复杂度是正确的(\(O(n\log^2{n})\)),因此没有必要进行李超线段树合并;而对于普通的李超线段树合并问题,每个节点都会挂一条线段,并且树的深度无法保证,所以就不能暴力考虑分别插入每条线段,否则时间复杂度会退化成 \(O(n^2\log{n})\),甚至不如暴力。

  • 树上链查询的问题可以考虑 出栈序。看似多考虑了节点信息,实则并没有真正查询到这些信息。

    一对时间戳在岔路间流动着。我的时间戳站在谷底接受漫天星火的审判。审判之下,岁月的鸿沟被过往湮灭于黑暗,那里是我将要驻足的地方。

#include<bits/stdc++.h>
#define LL long long
#define DB double
#define MOD 1000000007
#define ls(x) x << 1
#define rs(x) x << 1 | 1
#define lowbit(x) x & (-x)
#define PII pair<int, int>
#define PLI pair<LL, int>
#define MP make_pair
#define VI vector<int>
#define VII vector<int>::iterator
#define all(x) x.begin(), x.end()
#define EB emplace_back
#define SI set<int>
#define SII set<int>::iterator
#define QI queue<int>
using namespace std;
template<typename T> void chkmn(T &a, const T &b) { (a > b) && (a = b); }
template<typename T> void chkmx(T &a, const T &b) { (a < b) && (a = b); }
int inc(const int &a, const int &b) { return a + b >= MOD ? a + b - MOD : a + b; }
int dec(const int &a, const int &b) { return a - b < 0 ? a - b + MOD : a - b; }
int mul(const int &a, const int &b) { return 1LL * a * b % MOD; }
int sqr(const int &a) { return 1LL * a * a % MOD; }
void Inc(int &a, const int &b) { ((a += b) >= MOD) && (a -= MOD); }
void Dec(int &a, const int &b) { ((a -= b) < 0) && (a += MOD); }
void Mul(int &a, const int &b) { a = 1LL * a * b % MOD; }
void Sqr(int &a) { a = 1LL * a * a % MOD; }
int qwqmi(int x, int k = MOD - 2)
{
	int res = 1;
	while(k)
	{
		if(k & 1) Mul(res, x);
		Sqr(x), k >>= 1;
	}
	return res;
}
const int N = 2e5 + 5;
const int M = 3e7 + 5;
const int Range = 1e6 + 5;
const LL INF = 1e18;
int n, typ;
struct Line
{
	LL k, b;
}line[N];
LL F(int id, int x)
{
	return 1LL * line[id].k * x + line[id].b;
}
struct LiChaoSGT
{
	int dop;
	struct SegTree
	{
		int ls, rs;
		int id;
	}tr[M];
	void make_new(int &p, int id)
	{
		p = ++dop;
		tr[p].id = id;
	}
	void insert(int &p, int l, int r, int id)
	{
		if(!p) return (void)make_new(p, id);
		int mid = l + (r - l) / 2;
		if(F(id, mid) < F(tr[p].id, mid)) swap(id, tr[p].id);
		if(F(id, l) >= F(tr[p].id, l) && F(id, r) >= F(tr[p].id, r)) return;
		if(F(id, l) < F(tr[p].id, l)) insert(tr[p].ls, l, mid, id);
		else insert(tr[p].rs, mid + 1, r, id);
	}
	LL query(int p, int l, int r, int x)
	{
		if(!p) return INF;
		int mid = l + (r - l) / 2;
		LL res = F(tr[p].id, x);
		if(mid >= x) chkmn(res, query(tr[p].ls, l, mid, x));
		else chkmn(res, query(tr[p].rs, mid + 1, r, x));
		return res;
	}
}slopeT; // LCT (bushi)
int rt[M];
struct SGT
{
	void modify(int p, int l, int r, int x, int id)
	{
		slopeT.insert(rt[p], 0, Range, id);
		if(l == r) return;
		int mid = l + (r - l) / 2;
		if(mid >= x) modify(ls(p), l, mid, x, id);
		else modify(rs(p), mid + 1, r, x, id);
	}
	LL query(int p, int l, int r, int L, int R, int x)
	{
		if(l >= L && r <= R)
			return slopeT.query(rt[p], 0, Range, x);
		int mid = l + (r - l) / 2;
		LL res = INF;
		if(mid >= L) chkmn(res, query(ls(p), l, mid, L, R, x));
		if(mid < R) chkmn(res, query(rs(p), mid + 1, r, L, R, x));
		return res;
	}
}T;
vector<PLI> G[N];
int p[N];
LL d[N], q[N], l[N], dp[N];
int ord[N], timestamp;
int f[N][21], leapf[N];
LL s[N][21];
void predfs(int u, int fa)
{
	for(int i = 1; i < 21; ++i)
	{
		f[u][i] = f[f[u][i - 1]][i - 1];
		s[u][i] = s[u][i - 1] + s[f[u][i - 1]][i - 1];
	}
	leapf[u] = u;
	for(int i = 20; i >= 0; --i)
		if(s[leapf[u]][i] <= l[u] && f[leapf[u]][i])
		{
			l[u] -= s[leapf[u]][i];
			leapf[u] = f[leapf[u]][i];
		}
	for(auto nxt : G[u])
	{
		int v = nxt.second;
		LL w = nxt.first;
		if(v == fa)
			continue;
		f[v][0] = u;
		s[v][0] = w;
		d[v] = d[u] + w;
		predfs(v, u);
	}
	ord[u] = ++timestamp;
}
void dfs(int u, int fa)
{
	line[u] = (Line){-d[u], dp[u]};
	T.modify(1, 1, n, ord[u], u);
	for(auto nxt : G[u])
	{
		int v = nxt.second;
		if(v == fa)
			continue;
		dp[v] = T.query(1, 1, n, ord[v], ord[leapf[v]], p[v]) + 1LL * d[v] * p[v] + q[v];
		dfs(v, u);
	}
}
int main()
{
	scanf("%d %d", &n, &typ);
	for(int i = 2; i <= n; ++i)
	{
		int fa; LL w;
		scanf("%d %lld %d %lld %lld", &fa, &w, &p[i], &q[i], &l[i]);
		G[i].EB(MP(w, fa));
		G[fa].EB(MP(w, i));
	}
	predfs(1, 0);
	dfs(1, 0);
	for(int i = 2; i <= n; ++i)
		printf("%lld\n", dp[i]);
	return 0;
}

标签:const,int,mid,线段,购票,P2305,NOI2014,return,id
From: https://www.cnblogs.com/Schucking-Sattin/p/17677916.html

相关文章

  • [NOI2014] 起床困难综合症
    [NOI2014]起床困难综合症洛谷题目描述\(21\)世纪,许多人得了一种奇怪的病:起床困难综合症,其临床表现为:起床难,起床后精神不佳。作为一名青春阳光好少年,atm一直坚持与起床困难综合症作斗争。通过研究相关文献,他找到了该病的发病原因:在深邃的太平洋海底中,出现了一条名为drd的......
  • P2305 [NOI2014] 购票
    P2305[NOI2014]购票题意今年夏天,NOI在SZ市迎来了她三十周岁的生日。来自全国\(n\)个城市的OIer们都会从各地出发,到SZ市参加这次盛会。全国的城市构成了一棵以SZ市为根的有根树,每个城市与它的父亲用道路连接。为了方便起见,我们将全国的\(n\)个城市用\(1\simn\)......
  • 基于SSM的电影院购票系统开源啦
    大家好,今天给大家带来一款SSM的电影院售票系统,非常不错的一个项目,学习javaweb编程必备。下载地址在文末1.SpringMVCSpringMVC属于SpringFrameWork的后续产品,已经融合在SpringWebFlow里面。Spring框架提供了构建Web应用程序的全功能MVC模块。使用Spring可插入的MVC架构......
  • Luogu P2375 [NOI2014] 动物园
    [NOI2014]动物园题目描述近日,园长发现动物园中好吃懒做的动物越来越多了。例如企鹅,只会卖萌向游客要吃的。为了整治动物园的不良风气,让动物们凭自己的真才实学向游客要吃的,园长决定开设算法班,让动物们学习算法。某天,园长给动物们讲解KMP算法。园长:“对于一个字符串\(S\),它......
  • 电影购票系统相关
    用户故事用例图: 类图: 活动图: 设计结果: ......
  • [NOI2014]动物园
    [NOI2014]动物园这题看题目描述就知道一定是跟KMP扯上关系了。首先,如果不考虑长度超过\(\dfrac{1}{2}\)的限制的话,那么就很简单,每次求出一个新的\(ne_i\)时,如下图所示图中红色的表示目前对于前\(i\)个字符来说,最长公共前后缀为红色部分,因为两个红色部分中一定都有前后......
  • P4211 [LNOI2014]LCA
    \(\color{purple}\text{P4211[LNOI2014]LCA}\)解题方法可以发现一个结论:两个点到根节点的重合路径的长度即为他们\(LCA\)的深度。所以我们把\([l,r]\)之间的点到根节点路径上各加一,再查询\(z\)到根节点的路径的值之和即为\(\sum_{i=l}^{r}\text{dep}[\text{LCA}(i,z)]\)......
  • 基于SSM框架的航班购票系统
    @文章目录目录1、相关技术1.1、Javaweb1.2、SSM框架1.3、前端框架AngularJS1.4、数据库MySQL1.5、数据库Redis1.6、开发工具Eclipse2、需求分析2.1、系统实现目标2.2、系统功能分析2.3、系统用例图3、工程结构及其说明4、系统总体设计4.1、软件架构设计4.2、总体功能模块设计4.3......
  • [LNOI2014] LCA 树链剖分+离线处理+lca转化
    困困的开始了我的修炼树剖之旅途考虑怎么搞这个lca是说,习惯了倍增求lca,突然冒出这么一个东西还真不会搞那要么能一次性求很多个lca(?),要么把deep[lca(i,z)]这个东西转化......
  • P2375 [NOI2014] 动物园
    求num[i],表示1~i前缀的合法子串个数(满足前后缀相等,且不重合 #include<iostream>#include<cstring>usingnamespacestd;constintN=1e6+3,mod=1e9+7;......