四叠半神话大系(bfs序+st+在线倍增+二分)(北理16校赛)

时间限制1秒 内存限制64M

题目描述:

「我们大部分的烦恼,是来自于梦想另一种有可能的人生。把希望寄托于自己的可能性这种不能指望的东西,正是万恶的根源。」

时间突然停止。

我获得了可以在平行世界中穿梭的能力。

宇宙中存在着无数的平行世界,这些世界组成了一个树形的结构,每个世界有一个编号和一个幸福度。

每当我做出选择时,世界会分裂成若干个新的世界。选择会导致幸福度的变化。根据选择的不同,在新的世界里,我可能会变的更加幸福,或者更加不幸。

出于某些原因,我只能在处于相同时间的世界内穿梭,而且只能在由若干个时间单位前的世界分裂来的世界中穿梭。

我要在平行世界中找到一个最幸福的世界,在那里继续生活下去。然而真的存在这样的世界吗?

输入格式:

第一行,一个整数,表示一共有个世界。

接下来行,每行三个整数,.
表示号世界下一个时间单位内会走向号世界,幸福度会变化。()

接下来一行,一个整数,表示接下来有Q个询问。

接下来行,每行两个整数。表示现在身处号世界,允许穿梭到从个时间单位前分裂的所有世界。 ,  。

数据保证,1号世界是根节点,且1号节点的幸福度是

输出格式:

行,每行一个整数。第的整数表示第个询问中,能获得的最大幸福度。

样例输入:

8

1 2 1

2 3 3

1 4 2

1 5 -2

5 6 3

5 7 4

5 8 -2

3

6 1

6 2

5 1

样例输出:

2

4

2

样例解释:

对于第二个询问(6, 2),6号世界2个时间单位前的世界是1号世界,1号世界分裂的所有世界中,与6号世界处在相同时间的是3号、6号、7号、8号世界,其中幸福度最高的是3号世界(0 + 1 + 3 = 4)。

题目大意:

一棵有根树,每个节点有一个权值,现在有若干个询问,对于每一个询问(x,y),求与x节点有相同深度且具有相同第y级祖先的所有节点中,权值的最大值。

解题思路:

用类似在线倍增lca的思路,nlogn预处理,可以在logn内求出他的y级祖先。

相同深度而且有某个公共y级祖先的节点的bfs序是连续的,如果获得了bfs序的区间,可以用rmq求出这个区间节点中的最大权值。

先求出所有点的bfs序,构建这些节点的st,然后二分求出区间的左端点和右端点(随着bfs序的增大他们的y级祖先的bfs序也在增大)每次求祖先logn的,因此查询的复杂度是(logn)^2的,总的复杂度o(qlognlogn)

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<cmath>
using namespace std;
const int maxn = 100050;
const double eps = 1e-6;
int p[maxn][55], head[maxn], tot, val[maxn], vis[maxn], bfsxu, pos[maxn], spos[maxn], mm[maxn];
int maxx[maxn][55];
struct node {
	int from, to, next, val;
}e[maxn];
void init()
{
	tot = 0; bfsxu = 1; pos[1] = 1; spos[1] = 1;
	memset(head, -1, sizeof(head));
	memset(vis, 0, sizeof(vis));
	memset(val, 0, sizeof(val));
	memset(p, -1, sizeof(p));
}
void add(int u, int v, int w)
{
	e[tot].from = u;
	e[tot].to = v;
	e[tot].next = head[u];
	e[tot].val = w;
	head[u] = tot++;
}
void dfs(int u)
{
	vis[u] = 1;
	for (int i = head[u]; ~i; i = e[i].next)
	{
		int v = e[i].to;
		if (!vis[v])
		{
			p[v][0] = u;
			dfs(v);
		}
	}
}
void rmq(int n)
{
	for (int j = 1; (1 << j) <= n; j++)
		for (int i = 1; i <= n; i++)
			if (~p[i][j - 1])p[i][j] = p[p[i][j - 1]][j - 1];
}
void st(int n)
{
	mm[0] = -1;
	for (int i = 1; i <= n; i++)
	{
		mm[i] = ((i&(i - 1)) == 0) ? mm[i - 1] + 1 : mm[i - 1];
		maxx[i][0] = val[pos[i]];
	}
	for (int j = 1; j <= mm[n]; j++)
		for (int i = 1; i + (1 << j) - 1 <= n; i++)
			maxx[i][j] = max(maxx[i][j - 1], maxx[i + (1 << (j - 1))][j - 1]);
}
int lca(int x, int y)
{
	int pa = x;
	while (y&&pa != -1)
	{
		int low = y&(-y);
		pa = p[pa][mm[low]];
		y -= low;
	}
	if (pa == -1)return 0;
	return pa;
}
int solve(int x, int y)
{
	int k = mm[y - x + 1];
	return max(maxx[x][k], maxx[y - (1 << k) + 1][k]);
}
int main()
{
	int n, x, y, z, q;
	while (~scanf("%d", &n))
	{
		init();
		for (int i = 1; i < n; i++)
		{
			scanf("%d%d%d", &x, &y, &z);
			add(x, y, z);
		}
		dfs(1);
		rmq(n);
		queue<int>que;
		que.push(1);
		while (!que.empty())
		{
			int u = que.front(); que.pop();
			for (int i = head[u]; ~i; i = e[i].next)
			{
				int v = e[i].to;
				val[v] = val[e[i].from] + e[i].val;
				pos[++bfsxu] = v; spos[v] = bfsxu;
				que.push(v);
			}
		}
		st(n);
		scanf("%d", &q);
		while (q--)
		{
			scanf("%d%d", &x, &y);
			int fa = lca(x, y), ll, rr;
			int l = 1, r = spos[x];
			while (l<r)
			{
				int mid = (l + r) / 2;
				if (spos[lca(pos[mid], y)] < spos[fa])l = mid + 1;
				else r = mid;
			}
			ll = l;
			l = spos[x], r = n;
			while (l<r)
			{
				int mid = (l + r) / 2;
				if (spos[lca(pos[mid], y)]>spos[fa])r = mid - 1;
				else l = mid;
				if (r == l + 1)
				{
					if (spos[lca(pos[r], y)] == spos[fa])l = r;
					else r = l;
				}
			}
			rr = l;
			printf("%d\n", solve(ll, rr));
		}
	}
	return 0;
}
时间: 2024-09-29 13:18:09

四叠半神话大系(bfs序+st+在线倍增+二分)(北理16校赛)的相关文章

bnu 51641 Certain Maze(bfs)(北师16校赛)

最近,无聊的过河船同学发现了一种无聊的迷宫生成算法. 算法过程如下: 一个的矩形区域可以看作个单位网格组成.在每个网格中,随机生成一个从右上角到左下角的L型障碍或者从左上角到右下角的R型障碍(障碍可以被看作一条线段). 图1:两种障碍 这样便可以生成一个大小为的迷宫,如图2所示. 图2:无聊的迷宫 然后过河船同学想知道,是否存在迷宫内的从迷宫上边界到达迷宫的下边界的路径.于是无聊的过河船同学花了一夜的时间,终于找到一条路径. 图3:过河船同学辛辛苦苦找到的道路 痛苦的过河船同学不想再伤害自己的眼

【BZOJ3784】树上的路径 点分治序+ST表

[BZOJ3784]树上的路径 Description 给定一个N个结点的树,结点用正整数1..N编号.每条边有一个正整数权值.用d(a,b)表示从结点a到结点b路边上经过边的权值.其中要求a<b.将这n*(n-1)/2个距离从大到小排序,输出前M个距离值. Input 第一行两个正整数N,M 下面N-1行,每行三个正整数a,b,c(a,b<=N,C<=10000).表示结点a到结点b有一条权值为c的边. Output 共M行,如题所述. Sample Input 5 10 1 2 1

HDU5957 Query on a graph(拓扑找环,BFS序,线段树更新,分类讨论)

传送门:http://acm.hdu.edu.cn/showproblem.php?pid=5957 题意:D(u,v)是节点u和节点v之间的距离,S(u,v)是一系列满足D(u,x)<=k的点的集合,操作1:将S(u,k)内节点权值增加或者减小,操作2:查询S(u,k)内节点的权值和 题解:因为题目说了查询和更新的距离小于等于k,k最大为2,所以很显然要分情况讨论k为0.1.2的情况 因为是多次更新,我们显然是需要用线段树来维护节点权值的 运用线段树和bfs序的知识我们知道 对一个棵树求BFS

Android RecyclerView的item大小保持四个半

现在有这么一个需求,实现下图的UI.  我想你应该能想到用RecyclerView实现, 当我唰唰唰几分钟做完之后,UI设计师跟我说,每个item,无论在什么手机上,都要显示四个半,具体看下图. 我们都知道,Android手机的屏幕大小不一,这种需求我们不可能把图片的宽写死,或者写成wrap_content,这个时候就要在java代码中重新测量并且重新设置宽了.代码如下: LinearLayout.LayoutParams params = new LinearLayout.LayoutPara

UVA - 10410 Tree Reconstruction (根据dfs序和bfs序恢复一颗树)

题意: 分析: 这题一开始完全没有思路, 一直没有找出规律. 参考了http://www.cnblogs.com/Wade-/p/6358859.html 和 http://www.cnblogs.com/jerryRey/p/4622927.html 在dfs序列中,相邻的两个结点u,v之间(dfs_pos(u) + 1 =  dfs_pos(v)),有父子,兄弟,其他3种关系. 父子关系:在bfs中u,v并不是相邻, bfs_pos(v) > bfs_pos(u) , 那么u为v父亲, v为

51nod2621 树上距离一题四解ST表+倍增+Tarjan+树剖

LCA裸题 只有代码无原理,给自己复习用 1. ST表(这题2^10就够了) 1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int maxn=2e3+50; 5 6 int cnt,dfn[maxn],dep[maxn],dp[maxn][21],lg2[maxn],dis[maxn],w[maxn][maxn]; 7 std::vector<int> G[maxn]; 8 void dfs(int u,in

ST算法(倍增)(用于解决RMQ)

ST算法 在RMQ(区间最值问题)问题中,我了解到一个叫ST的算法,实质是二进制的倍增. ST算法能在O(nlogn)的时间预处理后,用O(1)的时间在线回答区间最值. f[i][j]表示从i位起的2^j个数中的最大(最小)数,即[i,i+2^j-1]中的最大(最小)值,从其定义中可以看出来. 下面的实现代码以最大值为例: 预处理: void preST(int len){ for(int i=1;i<=len;i++) f[i][0]=i; int m=log(len)/log(2)+1; f

树的dfs序,p1539,其他经典问题,2018/11/08模拟赛T3

树的dfs序指从根节点进行dfs(先序遍历),每次到达某个点的时间和离开这个点的时间.它可以将树上的问题转换成序列问题进行处理. 比如对于p1539的样例可以这样解释. 每个点的左边数字表示进入该点的"时间",右边的数字表示离开该点的"时间".对dfs序的介绍就到这里. 然后来看一个例题: 先读入边,跑一遍dfs确定dfs序. 对于操作1,把点x的进入的"时间"+=a,把x出去的"时间"-=a 这样操作3询问根节点到y的路径点

RMQ(st在线算法模板)

#include<iostream> #include<cmath> #include<algorithm> using namespace std; #define M 100010 #define MAXN 500 #define MAXM 500 int dp[M][18]; /* *一维RMQ ST算法 *构造RMQ数组 makermq(int n,int b[]) O(nlog(n))的算法复杂度 *dp[i][j] 表示从i到i+2^j -1中最小的一个值(