洛谷-【图论2-1】树6

P2680 [NOIP 2015 提高组] 运输计划

题目背景

NOIP2015 Day2T3

题目描述

公元 2044 年,人类进入了宇宙纪元。

L 国有 n 个星球,还有 n−1 条双向航道,每条航道建立在两个星球之间,这 n−1 条航道连通了 L 国的所有星球。

小 P 掌管一家物流公司, 该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从 ui​ 号星球沿最快的宇航路径飞行到 vi​ 号星球去。显然,飞船驶过一条航道是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为 tj​,并且任意两艘飞船之间不会产生任何干扰。

为了鼓励科技创新, L 国国王同意小 P 的物流公司参与 L 国的航道建设,即允许小 P 把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间。

在虫洞的建设完成前小 P 的物流公司就预接了 m 个运输计划。在虫洞建设完成后,这 m 个运输计划会同时开始,所有飞船一起出发。当这 m 个运输计划都完成时,小 P 的物流公司的阶段性工作就完成了。

如果小 P 可以自由选择将哪一条航道改造成虫洞, 试求出小 P 的物流公司完成阶段性工作所需要的最短时间是多少?

输入格式

第一行包括两个正整数 n,m,表示 L 国中星球的数量及小 P 公司预接的运输计划的数量,星球从 1 到 n 编号。

接下来 n−1 行描述航道的建设情况,其中第 i 行包含三个整数 ai​,bi​ 和 ti​,表示第 i 条双向航道修建在 ai​ 与 bi​ 两个星球之间,任意飞船驶过它所花费的时间为 ti​。

接下来 m 行描述运输计划的情况,其中第 j 行包含两个正整数 uj​ 和 vj​,表示第 j 个运输计划是从 uj​ 号星球飞往 vj​ 号星球。

输出格式

一个整数,表示小 P 的物流公司完成阶段性工作所需要的最短时间。

输入输出样例

输入 #1复制

复制代码
6 3 
1 2 3 
1 6 4 
3 1 7 
4 3 6 
3 5 5 
3 6 
2 5 
4 5

输出 #1复制

复制代码
11

说明/提示

所有测试数据的范围和特点如下表所示

测试点编号 n= m= 约定
1 100 1
2 100 100 第 i 条航道连接 i 号星球与 i+1 号星球
3 100 100
4 2000 1
5 1000 1000 第 i 条航道连接 i 号星球与 i+1 号星球
6 2000 2000 第 i 条航道连接 i 号星球与 i+1 号星球
7 3000 3000 第 i 条航道连接 i 号星球与 i+1 号星球
8 1000 1000
9 2000 2000
10 3000 3000
11 80000 1
12 100000 1
13 70000 70000 第 i 条航道连接 i 号星球与 i+1 号星球
14 80000 80000 第 i 条航道连接 i 号星球与 i+1 号星球
15 90000 90000 第 i 条航道连接 i 号星球与 i+1 号星球
16 100000 100000 第 i 条航道连接 i 号星球与 i+1 号星球
17 80000 80000
18 90000 90000
19 100000 100000
20 300000 300000
所有数据 1≤ai​,bi​,uj​,vj​≤n,0≤ti​≤1000

请注意常数因子带来的程序效率上的影响。

实现代码:

cpp 复制代码
#pragma warning (disable:4996)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define RG register
#define mid ((x+y)>>1)
#define lson (pst<<1)
#define rson (pst<<1|1)
using namespace std;
const int maxn = 3e5 + 5, maxm = maxn << 1, inf = 0x7fffffff;
int x[maxn], y[maxn], z[maxn], p[maxn];
int head[maxm], nxt[maxm], v[maxm], cnt;
int son[maxn], dad[maxn], sz[maxn], depth[maxn], root;
int id[maxn], top[maxn], rak[maxn], num;
int c[maxn], d[maxn], srt[maxn];
int ma, mb, mc;
int n, m;

struct Binary_Indexed_Tree
{
	int a[maxn];

	inline int lowbit(int k) { return k & (-k); }
	inline void update(int x, int k) { for (int i = x; i <= n; i += lowbit(i))	a[i] += k; }
	inline int query(int x) { int i = x, ans = 0; for (i = x; i >= 1; i -= lowbit(i))	ans += a[i]; return ans; }
	inline void build(int x) { for (int i = 1; i <= n; i++)	update(i, p[rak[i]]); }
	inline int sum(int l, int r) { return query(r) - query(l - 1); }
}BIT;

inline int max(int x, int y) { return x > y ? x : y; }
inline int min(int x, int y) { return x < y ? x : y; }

struct Segment_Tree
{
	int mx[maxn << 2], tag[maxn << 2];

	inline void pushdown(int pst)
	{
		if (!tag[pst])	return;
		int k = tag[pst];
		mx[lson] = max(mx[lson], k), mx[rson] = max(mx[rson], k);
		tag[lson] = max(tag[lson], k), tag[rson] = max(tag[rson], k);
		tag[pst] = 0; return;
	}

	inline void pushup(int pst) { mx[pst] = max(mx[lson], mx[rson]); }

	inline void update(int x, int y, int pst, int l, int r, int k)
	{
		if (x > y || y<l || x>r)	return;
		if (l <= x && y <= r) { mx[pst] = max(mx[pst], k), tag[pst] = max(tag[pst], k); return; }
		pushdown(pst);
		update(x, mid, lson, l, r, k), update(mid + 1, y, rson, l, r, k);
		pushup(pst); return;
	}

	inline int query(int x, int y, int pst, int p)
	{
		if (x == y)	return mx[pst];
		pushdown(pst);
		if (p <= mid)	return query(x, mid, lson, p);
		else return query(mid + 1, y, rson, p);
	}
}ST;

inline void addline(int x, int y) { v[cnt] = y, nxt[cnt] = head[x], head[x] = cnt++; }

inline int read()
{
	RG char c = getchar(); RG int x = 0;
	while (c<'0' || c>'9')	c = getchar();
	while (c >= '0'&&c <= '9')	x = (x << 3) + (x << 1) + c - '0', c = getchar();
	return x;
}

inline void dfs1(int x, int f, int d)
{
	dad[x] = f, depth[x] = d, sz[x] = 1;
	for (RG int i = head[x]; ~i; i = nxt[i])
	{
		if (v[i] == f)	continue;
		dfs1(v[i], x, d + 1);
		sz[x] += sz[v[i]];
		if (sz[v[i]] > sz[son[x]])	son[x] = v[i];
	}
	return;
}

inline void dfs2(int x, int t)
{
	top[x] = t, id[x] = ++num, rak[id[x]] = x;
	if (!son[x])	return;
	dfs2(son[x], t);
	for (RG int i = head[x]; ~i; i = nxt[i])
		if (v[i] != dad[x] && v[i] != son[x])	dfs2(v[i], v[i]);
	return;
}

inline int sum(int x, int y)
{
	RG int tx = top[x], ty = top[y], ans = 0;
	while (tx != ty)
	{
		if (depth[tx] >= depth[ty])	ans += BIT.sum(id[tx], id[x]), x = dad[tx], tx = top[x];
		else ans += BIT.sum(id[ty], id[y]), y = dad[ty], ty = top[y];
	}
	if (id[x] <= id[y])	ans += BIT.sum(id[x] + 1, id[y]);
	else ans += BIT.sum(id[y] + 1, id[x]);
	return ans;
}

inline bool cmp(int x, int y) { return c[x] < c[y]; }

inline void update(int x, int y, int z)
{
	RG int tx = top[x], ty = top[y], t = 0;
	while (tx != ty)
	{
		if (depth[tx] >= depth[ty]) c[++t] = id[tx], d[t] = id[x], x = dad[tx], tx = top[x];
		else c[++t] = id[ty], d[t] = id[y], y = dad[ty], ty = top[y];
	}
	if (id[x] <= id[y])	c[++t] = id[x] + 1, d[t] = id[y];
	else c[++t] = id[y] + 1, d[t] = id[x];
	for (int i = 1; i <= t; i++)	srt[i] = i;
	sort(srt + 1, srt + t + 1, cmp);
	if (c[srt[1]] > 1)	ST.update(1, n, 1, 1, c[srt[1]] - 1, z);
	if (d[srt[t]] < n)	ST.update(1, n, 1, d[srt[t]] + 1, n, z);
	for (int i = 1; i < t; i++)	ST.update(1, n, 1, d[srt[i]] + 1, c[srt[i + 1]] - 1, z);
	return;
}

inline int find_ans(int x, int y)
{
	RG int ans = inf;
	if (x == y)	return 0;
	if (depth[x] < depth[y])	swap(x, y);
	while (depth[x] != depth[y])	ans = min(ans, max(mc - p[x], ST.query(1, n, 1, id[x]))), x = dad[x];
	while (x != y)
	{
		if (depth[x] > depth[y])	ans = min(ans, max(mc - p[x], ST.query(1, n, 1, id[x]))), x = dad[x];
		else ans = min(ans, max(mc - p[y], ST.query(1, n, 1, id[y]))), y = dad[y];
	}
	return ans;
}

int main(void)
{
	memset(head, -1, sizeof(head));
	n = read(), m = read();
	for (int i = 1; i < n; i++)	x[i] = read(), y[i] = read(), z[i] = read();
	for (int i = 1; i < n; i++)	addline(x[i], y[i]), addline(y[i], x[i]);
	root = rand() % n + 1, dfs1(root, 0, 1), dfs2(root, root);
	for (int i = 1; i < n; i++)
	{
		if (depth[x[i]] > depth[y[i]])	p[x[i]] = z[i];
		else p[y[i]] = z[i];
	}
	BIT.build(n);
	for (int i = 1; i <= m; i++)
	{
		RG int a = read(), b = read(), temp;
		temp = sum(a, b), update(a, b, temp);
		if (temp >= mc)	ma = a, mb = b, mc = temp;
	}
	printf("%d\n", find_ans(ma, mb));
	return 0;
}

P1600 [NOIP 2016 提高组] 天天爱跑步

题目背景

NOIP2016 提高组 D1T2

题目描述

小 C 同学认为跑步非常有趣,于是决定制作一款叫做《天天爱跑步》的游戏。《天天爱跑步》是一个养成类游戏,需要玩家每天按时上线,完成打卡任务。

这个游戏的地图可以看作一棵包含 n 个结点和 n−1 条边的树,每条边连接两个结点,且任意两个结点存在一条路径互相可达。树上结点编号为从 1 到 n 的连续正整数。

现在有 m 个玩家,第 i 个玩家的起点为 si​,终点为 ti​。每天打卡任务开始时,所有玩家在第 0 秒同时从自己的起点出发,以每秒跑一条边的速度,不间断地沿着最短路径向着自己的终点跑去,跑到终点后该玩家就算完成了打卡任务。(由于地图是一棵树,所以每个人的路径是唯一的)

小 C 想知道游戏的活跃度,所以在每个结点上都放置了一个观察员。在结点 j 的观察员会选择在第 wj​ 秒观察玩家,一个玩家能被这个观察员观察到当且仅当该玩家在第 wj​ 秒也正好到达了结点 j。小 C 想知道每个观察员会观察到多少人?

注意:我们认为一个玩家到达自己的终点后该玩家就会结束游戏,他不能等待一段时间后再被观察员观察到。即对于把结点 j 作为终点的玩家:若他在第 wj​ 秒前到达终点,则在结点 j 的观察员不能观察到该玩家;若他正好在第 wj​ 秒到达终点,则在结点 j 的观察员可以观察到这个玩家。

输入格式

第一行有两个整数 n 和 m。其中 n 代表树的结点数量,同时也是观察员的数量,m 代表玩家的数量。

接下来 n−1 行每行两个整数 u 和 v,表示结点 u 到结点 v 有一条边。

接下来一行 n 个整数,其中第 j 个整数为 wj​,表示结点 j 出现观察员的时间。

接下来 m 行,每行两个整数 si​,和 ti​,表示一个玩家的起点和终点。

对于所有的数据,保证 1≤si​,ti​≤n,0≤wj​≤n。

输出格式

输出 1 行 n 个整数,第 j 个整数表示结点 j 的观察员可以观察到多少人。

输入输出样例

输入 #1复制

复制代码
6 3
2 3
1 2 
1 4 
4 5 
4 6 
0 2 5 1 2 3 
1 5 
1 3 
2 6 

输出 #1复制

复制代码
2 0 0 1 1 1 

输入 #2复制

复制代码
5 3 
1 2 
2 3 
2 4 
1 5 
0 1 0 3 0 
3 1 
1 4
5 5 

输出 #2复制

复制代码
1 2 1 0 1 

说明/提示

样例 1 说明

对于 1 号点,wi​=0,故只有起点为 1 号点的玩家才会被观察到,所以玩家 1 和玩家 2 被观察到,共有 2 人被观察到。

对于 2 号点,没有玩家在第 2 秒时在此结点,共 0 人被观察到。

对于 3 号点,没有玩家在第 5 秒时在此结点,共 0 人被观察到。

对于 4 号点,玩家 1 被观察到,共 1 人被观察到。

对于 5 号点,玩家 1 被观察到,共 1 人被观察到。

对于 6 号点,玩家 3 被观察到,共 1 人被观察到。

子任务

每个测试点的数据规模及特点如下表所示。

提示:数据范围的个位上的数字可以帮助判断是哪一种数据类型。

测试点编号 n= m= 约定
1∼2 991 991 所有人的起点等于自己的终点,即 ∀i, si​=ti​
3∼4 992 992 所有 wj​=0
5 993 993
6∼8 99994 99994 ∀i∈[1,n−1],i 与 i+1 有边。即树退化成 1,2,...,n 按顺序连接的链
9∼12 99995 99995 所有 si​=1
13∼16 99996 99996 所有 ti​=1
17∼19 99997 99997
20 299998 299998

提示

(提示:由于原提示年代久远,不一定能完全反映现在的情况,现在已经对该提示做出了一定的修改,提示的原文可以在该剪贴板查看)

在最终评测时,调用栈占用的空间大小不会有单独的限制,但在我们的工作环境中默认会有 1MiB 的限制。 这可能会引起函数调用层数较多时,程序发生栈溢出崩溃 ,程序中较深层数的递归往往会导致这个问题。如果你的程序需要用到较大的栈空间,请务必注意该问题。

我们可以使用一些方法修改调用栈的大小限制。

  • Linux

我们可以在终端中输入下列命令:ulimit -s 1048576。此命令的意义是,将调用栈的大小限制修改为 1048576KiB=1GiB。

例如,对于如下程序 sample.cpp

复制代码
#include <bits/stdc++.h>
using namespace std;
int f[1000005];
void dfs(int a){
	if(a == 0){
		f[a] = 0;
		return;
	}
	dfs(a - 1);
	f[a] = f[a - 1] + 1;
}
int main(){
	dfs(1000000);
	return 0;
}

将上述源代码用命令 g++ sample.cpp -o sample 编译为可执行文件 sample 后,使用 ./sample 执行程序。

如果在没有使用命令 ulimit -s 1048576 的情况下运行该程序,sample 会因为栈溢出而崩溃;如果使用了上述命令后运行该程序,该程序则不会崩溃。

特别地,当你打开多个终端时,它们并不会共享该命令,你需要分别对它们运行该命令。

请注意,调用栈占用的空间会计入总空间占用中,和程序其他部分占用的内存共同受到内存限制。

  • Windows

如果你使用 Windows 下的 Dev-C++,请选择 工具-编译选项 并在如下区域填入以下命令 -Wl,--stack=1073741824,填入后注意确认"编译时加入以下命令的"的框是已勾选状态。

此处 1073741824 的单位是 B/Bytes。

实现代码:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int SIZE=300000;
int n, m, tot, h[SIZE], deep[SIZE], fa[SIZE][20], w[SIZE];
struct edge
{
	int to, next;
}E[SIZE*2], e1[SIZE*2], e2[SIZE*2];

void add(int x, int y)
{
	E[++tot].to=y;
	E[tot].next=h[x];
	h[x]=tot;
}

int tot1, tot2, h1[SIZE], h2[SIZE];
void add1(int x, int y)
{
	e1[++tot1].to=y;
	e1[tot1].next=h1[x];
	h1[x]=tot1;
}
void add2(int x, int y)
{
	e2[++tot2].to=y;
	e2[tot2].next=h2[x];
	h2[x]=tot2;
}

void dfs1(int x)
{
	for(int i=1; (1<<i)<=deep[x]; i++)
		fa[x][i]=fa[fa[x][i-1]][i-1];
	for(int i=h[x]; i; i=E[i].next)
	{
		int y=E[i].to;
		if(y==fa[x][0])	continue;
		fa[y][0]=x;
		deep[y]=deep[x]+1;
		dfs1(y);
	}
}

int get_lca(int x, int y)
{
	if(x==y) return x;
	if(deep[x]<deep[y]) swap(x, y);
	int t=log(deep[x]-deep[y])/log(2);
	for(int i=t; i>=0; i--)
	{
		if(deep[fa[x][i]]>=deep[y])
			x=fa[x][i];
		if(x==y)
			return x;
	}
	t=log(deep[x])/log(2);
	for(int i=t; i>=0; i--)
	{
		if(fa[x][i]!=fa[y][i])
			x=fa[x][i], y=fa[y][i];
	}
	return fa[x][0];
}

int b1[SIZE*2], b2[SIZE*2], js[SIZE], dist[SIZE], s[SIZE], t[SIZE], l[SIZE], ans[SIZE];
void dfs2(int x)
{
	int t1=b1[w[x]+deep[x]], t2=b2[w[x]-deep[x]+SIZE];
	for(int i=h[x]; i; i=E[i].next)
	{
		int y=E[i].to;
		if(y==fa[x][0]) continue;
		dfs2(y);
	}
	b1[deep[x]]+=js[x];
	for(int i=h1[x]; i; i=e1[i].next)
	{
		int y=e1[i].to;
		b2[dist[y]-deep[t[y]]+SIZE]++;
	}
	ans[x]+=b1[w[x]+deep[x]]-t1+b2[w[x]-deep[x]+SIZE]-t2;
	for(int i=h2[x]; i; i=e2[i].next)
	{
		int y=e2[i].to;
		b1[deep[s[y]]]--;
		b2[dist[y]-deep[t[y]]+SIZE]--;
	}
}

int main()
{
	scanf("%d%d", &n, &m);
	for(int i=1; i<n; i++)
	{
		int u, v;
		scanf("%d%d", &u, &v);
		add(u, v);
		add(v, u);
	}
	deep[1]=1;
	fa[1][0]=1;
	dfs1(1);
	for(int i=1; i<=n; i++) scanf("%d", &w[i]);
	for(int i=1; i<=m; i++)
	{
		scanf("%d%d", &s[i], &t[i]);
		int lca=get_lca(s[i], t[i]);
		dist[i]=deep[s[i]]+deep[t[i]]-2*deep[lca];
		js[s[i]]++;
		add1(t[i], i);
		add2(lca, i);
		if(deep[lca]+w[lca]==deep[s[i]]) ans[lca]--;
	}
	dfs2(1);
	for(int i=1; i<=n; i++) printf("%d ", ans[i]);
	return 0;
}

P3178 [HAOI2015] 树上操作

题目描述

有一棵点数为 N 的树,以点 1 为根,且树有点权。然后有 M 个操作,分为三种:

  • 操作 1:把某个节点 x 的点权增加 a。
  • 操作 2:把某个节点 x 为根的子树中所有点的点权都增加 a。
  • 操作 3:询问某个节点 x 到根的路径中所有点的点权和。

输入格式

第一行包含两个整数 N,M。表示点数和操作数。

接下来一行 N 个整数,表示树中节点的初始权值。

接下来 N−1 行每行两个正整数 from,to, 表示该树中存在一条边 (from,to)。

再接下来 M 行,每行分别表示一次操作。其中第一个数表示该操作的种类,之后接这个操作的参数。

输出格式

对于每个询问操作,输出该询问的答案。答案之间用换行隔开。

输入输出样例

输入 #1复制

复制代码
5 5
1 2 3 4 5
1 2
1 4
2 3
2 5
3 3
1 2 1
3 5
2 1 2
3 3

输出 #1复制

复制代码
6
9
13

说明/提示

对于 100% 的数据,1≤N,M≤105,且所有输入数据的绝对值都不会超过 106。

实现代码:

cpp 复制代码
#include <cstdio>
#include <cstring>
#define LL long long
#define root 1, 1, n
#define ls now << 1, l, mid
#define rs now << 1 | 1, mid + 1, r
using namespace std;
const int MAXN = 100001;
int n, m, cnt, tot;
int head[MAXN], next[MAXN << 1], to[MAXN << 1], tid[MAXN], size[MAXN];
LL a[MAXN << 2], b[MAXN << 2], val[MAXN], dis[MAXN];
inline void add(int x, int y)
{
    to[cnt] = y;
    next[cnt] = head[x];
    head[x] = cnt++;
} 
inline void dfs(int u)
{
    int i, v; 
    tid[u] = ++tot;
    size[u] = 1;
    for(i = head[u]; i != -1; i = next[i])
    {
        v = to[i];
        if(!size[v])
        {
            dis[v] = dis[u] + 1;
            dfs(v);
            size[u] += size[v];
        }
    }
}
inline void push_down(int now)
{
    a[now << 1] += a[now];
    a[now << 1 | 1] += a[now];
    b[now << 1] += b[now];
    b[now << 1 | 1] += b[now];
    a[now] = b[now] = 0;
}
inline void update(LL x, LL y, int ql, int qr, int now, int l, int r)
{
    if(ql <= l && r <= qr)
    {
        a[now] += x;
        b[now] += y;
        return;
    }
    push_down(now);
    int mid = (l + r) >> 1;
    if(ql <= mid) update(x, y, ql, qr, ls);
    if(mid < qr) update(x, y, ql, qr, rs);
}
inline LL query(int u, int x, int now, int l, int r)
{
    if(l == r) return dis[u] * a[now] + b[now];
    push_down(now);
    int mid = (l + r) >> 1;
    if(x <= mid) return query(u, x, ls);
    else return query(u, x, rs);
}
int main()
{
    int i, x, z;
    LL y;
    scanf("%d %d", &n, &m);
    for(i = 1; i <= n; i++)    scanf("%lld", &val[i]);
    memset(head, -1, sizeof(head));
    for(i = 1; i < n; i++)
    {
        scanf("%d %d", &x, &y);
        add(x, y);
        add(y, x);
    }
    dis[1] = 1;
    dfs(1);
    for(i = 1; i <= n; i++) update(0, val[i], tid[i], tid[i] + size[i] - 1, root);
    for(i = 1; i <= m; i++)
    {
        scanf("%d %d", &z, &x);
        if(z == 1)
        {
            scanf("%lld", &y);
            update(0, y, tid[x], tid[x] + size[x] - 1, root);
        }
        else if(z == 2)
        {
            scanf("%lld", &y);
            update(y, -((dis[x] - 1) * y), tid[x], tid[x] + size[x] - 1, root);
        }
        else printf("%lld\n", query(x, tid[x], root));
    }
    return 0;
}
相关推荐
被AI抢饭碗的人1 小时前
C++过渡Python
开发语言·python
sali-tec1 小时前
C# 基于OpenCv的视觉工作流-章73-点-线距离
图像处理·人工智能·opencv·算法·计算机视觉
不知名的老吴1 小时前
在C++中不用宏怎么打日志的使用建议
开发语言·c++·算法
jieyucx1 小时前
Go 语言进阶:结构体指针、new 关键字与匿名结构体/成员详解
开发语言·后端·golang·结构体
YL200404261 小时前
044二叉搜索树中第K小的元素
数据结构·leetcode
图码1 小时前
生命游戏的优雅解法:从O(mn)空间到O(1)空间的进阶之旅
数据结构·算法·游戏·矩阵·空间计算
wjs20241 小时前
jEasyUI 添加复选框指南
开发语言
迪霸LZTXDY2 小时前
U-NET模型训练--图像标注脚本工具
开发语言·python
码界筑梦坊2 小时前
119-基于Python的各类企业排行数据可视化分析系统
开发语言·python·信息可视化·数据分析·毕业设计·echarts·fastapi