首页 > 其他分享 >洛谷P4069 [SDOI2016] 游戏

洛谷P4069 [SDOI2016] 游戏

时间:2024-03-07 17:55:50浏览次数:28  
标签:洛谷 int st dfn SDOI2016 return P4069 times dis

题目描述

我们要操作的是一条在树上的路径\(s\)->\(t\)。

(1)查询\(s\)->\(t\)最大的数字。

(2)在\(s\)->\(t\)上增加一个数字,输入\(a\),\(b\),对于路径上的一个点\(u\)增加的数字是\(dis(s, u) \times a + b\)。

解题思路

直接查询一条从\(s\)到\(t\)的路径是十分不方便的,所以我们用树链剖分,这样就是走从\(s\)到\(t\)的所有链,然后维护链上的值,并且一条条查询,然后统一起来就是答案。

于是我们可以自然的想到找\(s\),\(t\)的\(lca\),因为这样才能拆成两个部分,然后分别维护上面的链。

我们的式子长得像一个一元函数),于是对于每一条单独的链,我们有两种情况。

令\(dis[u]\)表示从根节点到u的距离

(1)\(u\)在\(s\)->\(lca\)

\(add=(dis[s]-dis[u])\times a + b\)

即\(-dis[u] \times a + dis[s]\times a + b\)

(2)\(u\)在\(lca\)->\(t\)
\(add = (dis[s] + dis[u] - 2\times dis[lca]) \times a + b\)

这两个式子都是一个一次函数,而且我们要寻找其中某一个区间的最小值,所以可以使用李超线段树

关于细节

  • 传统的李超线段树是增加一条线段单点查询。对于这个题目,就算是在同一条链上,但是它们的\(dis\)并非是连续的,也就是说李超线段树的\(x\)轴用\(dis\)是不合理的。那么对于一条链而言有什么东西是连续的呢?那就是\(dfn\),尽管链与链之间的dfn可能不连续,但是我们本身就是单独的维护一条条链,所以这不重要。
  • 树链剖分的跳法就是先找到\(lca\),然后比较\(x\)的\(top\)(也就是链的顶端节点,如果自己就是顶端,那么\(top\)是自己)是否相等,如果相等就不跳了,不相等就继续往上跳(链顶端可以跳到自己的父亲节点)
  • 李超线段树存储的是dfn,所以说我们calc具体的函数值时,需要用另外的数组表示对应dfn下的距离。
  • 李超线段树是标记永久化的,所以不能只计算最后在满足区间的返回的\(min\)函数值,而是要把经过的所有的区间的都计算一便(这里注意将自己的边界和区间的边界比较,因为是一次函数,所以直接取端点就可以了)。
  • 树剖中的点往上跳的时候,点是会被改变的,但是计算所需要的是原来的点,所以要提前存一下)
  • st表找最近公共祖先,长度\(n\times 2\)才是边界,不是\(n\)
  • 数组不要开小了,st和fp关于st表的是两倍,边是两倍,李超线段树的slo和mn都是四倍,记得开long long
  • 李超的时间是\((log_{2}n)^2\),原因是外面要寻找满足的区间,找到了以后,内部的线段又需要单独更新。树链剖分是\(log_{2}n\)的。我们从下往上走,如果经过了一条轻边,那么我们整个子树的大小会增加至少两倍,那么我们最多经过\(log_{2}n\)条轻边,如果是重链的话,我们可以直接跳过,所以是\(log_{2}n\)的。
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
typedef long long ll;
const ll inf = 123456789123456789;
int n, cnt = 0, vis[N], top[N], son[N], dep[N], head[N], dfn[N], st[N << 1][21], len = 0, fp[N], L[N << 1];
int scnt = 0;
ll dis[N], d[N], par[N];
int h = 0;
ll mn[N << 2];
int slo[N << 2];
int m, tot = 0;
struct edge 
{
	int next, to;
	ll w;
}e[N << 1];
struct slope
{
	ll k, b;
}sl[N << 1];
void add_edge(int u, int v, int w)
{
	e[++ cnt].next = head[u], head[u] = cnt, e[cnt].to = v, e[cnt].w = w;
	return ;
}
void add_slope(ll k, ll b)
{
	sl[++ scnt].k = k, sl[scnt].b = b;
	return ;
}
ll calc(int x, int p)
{
	if(p == 0) return inf;
	return d[x] * sl[p].k + sl[p].b;
}
bool cmp(ll x, ll y)
{
	if(x > y) return true;
	return false;
}
int dfs1(int x, int last)
{
	int siz = 1, mx = 0;
	st[++ len][0] = x, fp[x] = len, par[x] = last;
	dep[x] = dep[last] + 1; 
	for (int i = head[x]; i; i = e[i].next)
	{
		int y = e[i].to;
		if(y == last) continue;
		dis[y] = dis[x] + e[i].w;
		int s = dfs1(y, x);
		if(mx < s) son[x] = y, mx = s;
		siz += s;	
		st[++ len][0] = x;
	}
	return siz;
}
void dfs2(int x, int last, int ancestor)
{
	if(!x) return ;
	dfn[x] = ++ tot, d[tot] = dis[x];
	top[x] = ancestor, dfs2(son[x], x, ancestor);
	for (int i = head[x]; i; i = e[i].next)
	{
		int y = e[i].to;
		if(dfn[y]) continue;
		dfs2(y, x, y);
	}
	return ;
}
void build_st()
{
	for (int j = 1; (1 << j) <= len; ++ j)
	{
		for (int i = 1; i + (1 << j) - 1 <= len; ++ i)
		{
			int a = st[i][j - 1], b = st[i + (1 << (j - 1))][j - 1];
			st[i][j] = dep[a] < dep[b] ? a : b;
		}
	}
	return ;
}
int lca(int x, int y)
{
	x = fp[x], y = fp[y];
	
	if(x > y) swap(x, y);
	int l = L[y - x + 1];
	int a = st[x][l], b = st[y - (1 << l) + 1][l];
	return dep[a] < dep[b] ? a : b; 
}

void build_tree(int p, int l, int r)
{
	mn[p] = inf, slo[p] = 0;
	if(l == r) return ;
	int mid = (l + r) >> 1;
	build_tree(p << 1, l, mid);
	build_tree(p << 1 | 1, mid + 1, r);
	return ;
}
void update(int p, int l, int r, int u)
{
	if(l == r)
	{
		mn[p] = min(mn[p], calc(l, u));
		return ;
	}
	int &v = slo[p];
	int mid = (l + r) >> 1;
	if(cmp(calc(mid, v), calc(mid, u))) swap(u, v);
	if(cmp(calc(l, v), calc(l, u))) update(p << 1, l, mid, u);
	if(cmp(calc(r, v), calc(r, u))) update(p << 1 | 1, mid + 1, r, u);
	mn[p] = min(min(min(calc(l, slo[p]), calc(r, slo[p])), mn[p << 1]), mn[p << 1 | 1]);
	return ;
}
void add_lichao(int p, int l, int r, int L, int R, int u)
{
	if(L <= l && r <= R) 
	{
		update(p, l, r, u);
		return ;
	}
	if(l > R || r < L) return ;
	int mid = (l + r) >> 1;
	add_lichao(p << 1, l, mid, L, R, u);
	add_lichao(p << 1 | 1, mid + 1, r, L, R, u);
	mn[p] = min(mn[p], min(mn[p << 1], mn[p << 1 | 1]));
	return ;
}
ll query_lichao(int p, int l, int r, int L, int R)
{
	if(L <= l && r <= R) return mn[p];
	if(l > R || r < L) return inf;
	ll ans = min(calc(max(l, L), slo[p]), calc(min(R, r), slo[p])); 
	int mid = (l + r) >> 1;
	return min(min(query_lichao(p << 1, l, mid, L, R), query_lichao(p << 1 | 1, mid + 1, r, L, R)), ans);
}
void add_tree_line(int l, int r, ll k, ll b)
{
	add_slope(k, b);
	add_lichao(1, 1, n, l, r, scnt);
	return ;
}

void add_line(int s, int t, int a, int b) 
{
	int lc = lca(s, t), S = s;
	while(top[s] != top[lc]) add_tree_line(dfn[top[s]], dfn[s],  -a, a * dis[S] + b), s = par[top[s]]; //这里有一个and,是为了排除其中一个节点就是lca的情况 
	add_tree_line(dfn[lc], dfn[s], -a, a * dis[S] + b);
	while(top[t] != top[lc]) add_tree_line(dfn[top[t]], dfn[t], a, a * dis[S] - 2 * dis[lc] * a + b), t = par[top[t]];
	add_tree_line(dfn[lc], dfn[t], a, a * dis[S] - 2 * dis[lc] * a + b);
	return ;
}
ll query(int s, int t)
{
	int lc = lca(s, t);
	ll ans = inf;
	while(top[s] != top[lc]) ans = min(ans, query_lichao(1, 1, n, dfn[top[s]], dfn[s])), s = par[top[s]];
	ans = min(ans, query_lichao(1, 1, n, dfn[lc], dfn[s]));
	while(top[t] != top[lc]) ans = min(ans, query_lichao(1, 1, n, dfn[top[t]], dfn[t])), t = par[top[t]];
	ans = min(ans, query_lichao(1, 1, n, dfn[lc], dfn[t]));
	return ans;	  
}
int main()
{
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= n * 2; ++ i) L[i] = (int)log2(i); 
	for (int i = 1; i <= n - 1; ++ i) 
	{
		int u, v;
		ll w;
		scanf("%d %d %lld", &u, &v, &w);
		add_edge(u, v, w);
		add_edge(v, u, w);
	}
	dfs1(1, 0);
	dfs2(1, 0, 1);
	build_st();
	build_tree(1, 1, n);
	while(m --)
	{
		int opt;
		scanf("%d", &opt);
		if(opt == 1)
		{
			int s, t;
			ll a, b;
			scanf("%d %d %lld %lld", &s, &t, &a, &b);
			add_line(s, t, a, b);
		}
		else
		{
			int s, t;
			scanf("%d %d", &s, &t);
			printf("%lld\n", query(s, t));
		}
	}
	return 0;
}

标签:洛谷,int,st,dfn,SDOI2016,return,P4069,times,dis
From: https://www.cnblogs.com/ybC202444/p/18059442

相关文章

  • 洛谷题单指南-搜索-P1101 单词方阵
    原题链接:https://www.luogu.com.cn/problem/P1101题意解读:对于方阵中的每一个字符,在8个方向上判断是否和"yizhong"匹配,是一个递归问题。解题思路:用chara[N][N]存储所有字符方阵,用boolb[N][N]标记每个字符是否在任一方向上和yizhong匹配遍历方阵每一字符,如果是'y'则在8个方......
  • 洛谷题单指南-搜索-P1019 [NOIP2000 提高组] 单词接龙
    原题链接:https://www.luogu.com.cn/problem/P1019题意解读:要计算接龙能得到的最长字符串,可以通过DFS暴搜所有可能的接龙方案解题思路:DFS的关键在于两个判断:1、下一个单词是否可以和上一个单词接龙,最短公共长度是多少(只需要看两个单词的最短公共长度,这样能保证接龙更长)2、单词......
  • 洛谷题单指南-搜索-P1605 迷宫
    原题链接:https://www.luogu.com.cn/problem/P1605题意解读:从起点走到终点的方案数,DFS可遍历所有情况。解题思路:在DFS过程中,有两种标记墙:不能访问已访问过的,不能重复访问定义数组inta[N][N]表示迷宫,1是墙或者已访问过的,0是可以通过的。100分代码:#include<bits/stdc++.h>......
  • 洛谷题单指南-搜索-P1433 吃奶酪
    原题链接:https://www.luogu.com.cn/problem/P1433题意解读:计算经过所有奶酪一次的总路径最短,可以采用dfs、dp等方法。解题思路:最直接的思路是DFS,暴搜所有的路径方案,计算最小距离,n最大是15,复杂度为15!≈10^12,必定会超时,先保证正确性,得到部分分:50分代码:#include<bits/stdc++.h......
  • 【洛谷】明明的随机数(双指针去除重复元素)
    题目描述代码:#include<iostream>#include<algorithm>usingnamespacestd;intmain(){ intn; cin>>n; intA[n]; for(inti=0;i<n;i++){ cin>>A[i]; } sort(A,A+n); intslow=0,fast=0; while(fast<n){ if(slow!=......
  • 【洛谷】求第k小的数字(分治算法)
    题目描述如题所述,找到n个数中第K小的数字。但是不同的是时间复杂度要求为O(n),也就是说基本上所有的排序算法都不能用了。这里适合的算法是分治法,也就是使用快速排序。因为这道题的一个特点是只需要得到第k小的数字,而并没有说要对所有元素进行排序。如果我们把所有小于某个元素......
  • 洛谷题单指南-搜索-P2895 [USACO08FEB] Meteor Shower S
    原题链接:https://www.luogu.com.cn/problem/P2895题意解读:所谓安全格子,就是在所有流星坠落之后,没有被烧焦的格子,要计算从起点到这些格子任一一个的最短路径,BFS可以解决。解题思路:1、读取数据,先把所有流星坠落点以及周围被烧焦的格子进行标记,得到安全格子2、从起点开始BFS,每走......
  • 洛谷题单指南-搜索-P1135 奇怪的电梯
    原题链接:https://www.luogu.com.cn/problem/P1135题意解读:计算A到B至少要按几次电梯,本质上就是求A到B的最短路径,可以通过BFS解决。解题思路:位于每一层,有两种选择:向上、向下BFS搜索直接从A找到B,每扩展一层,层数+1,层数即按电梯次数100分代码:#include<bits/stdc++.h>usingnam......
  • 洛谷题单指南-搜索-P1443 马的遍历
    原题链接:https://www.luogu.com.cn/problem/P1443题意解读:无论是国际象棋还是中国象棋,马的活动范围都是一样的:只不过国际象棋棋子是在格子中,中国象棋棋子是在交点,坐标的变化方式是一样的,根据此活动范围,计算马到达每一个点的最短路径。解题思路:根据马的活动范围,在棋盘内进行B......
  • 洛谷题单指南-搜索-P2392 kkksc03考前临时抱佛脚
    原题链接:https://www.luogu.com.cn/problem/P2392解题思路:参考https://www.cnblogs.com/jcwy/p/18003097前面已经给出了二进制法的代码,这里给出DFS的代码100分代码:#include<bits/stdc++.h>usingnamespacestd;constintN=25;ints1,s2,s3,s4;inta[N],b[N],c[......