【题解】LuoGu3787:冰精冻西瓜

版权声明:Fashion Education https://blog.csdn.net/ModestCoder_/article/details/87027358


原题传送门

题目描述
琪露诺是拥有操纵冷气程度的能力的妖精,一天她发现了一片西瓜地。这里有n个西瓜,由n-1条西瓜蔓连接,形成一个有根树,琪露诺想要把它们冷冻起来慢慢吃。

这些西瓜蔓具有神奇的性质,可以将经过它的冷气的寒冷程度放大或缩小,每条西瓜蔓放大/缩小冷气寒冷程度的能力值为Wi,表示冷气经过它后,寒冷程度值x会变为x*wi。每个西瓜也有一个寒冷程度值,炎热的夏日,所有西瓜的寒冷程度值初始都为0。

琪露诺会做出两种动作:

①.对着西瓜i放出寒冷程度为x的冷气。这股冷气顺着西瓜蔓向“西瓜树”的叶子节点蔓延,冷气的寒冷程度会按照上面的规则变化。遇到一个西瓜连了多条西瓜蔓时,每条叶子节点方向的西瓜蔓均会获得与原先寒冷程度相等的冷气。途径的所有西瓜的寒冷程度值都会加上冷气的寒冷程度值。

⑨.向你询问西瓜i的寒冷程度值是多少。

等等,为什么会有⑨?因为笨蛋琪露诺自己也会忘记放了多少冰呢。

所以,帮她计算的任务就这么交给你啦。

输入输出格式
输入格式:
第一行一个整数n,表示西瓜的数量。

西瓜编号为1~n,1为这棵“西瓜树”的根。

接下来n-1行,每行有两个整数u,v和一个实数w,表示西瓜u和西瓜v之间连接有一条藤蔓,它放大/缩小冷气寒冷程度的能力值为w。

接下来一行一个整数m,表示操作的数量。

接下来m行,每行两个或三个整数。

第一个数只能是1或9。

如果为1,接下来一个整数i和一个实数x,表示对西瓜i放出寒冷程度为x的冷气。

如果为9,接下来一个整数i,表示询问编号为i的西瓜的寒冷程度值。

输出格式:
对于每个操作⑨,输出一行一个实数,表示对应西瓜的寒冷程度值。

输入输出样例
输入样例#1:
4
1 2 1.00000000
2 3 0.00000000
3 4 1.00000101
9
1 1 3.00000000
9 2
9 3
1 2 1.42856031
9 4
9 2
1 3 4.23333333
9 2
9 4
输出样例#1:
3.00000000
0.00000000
0.00000000
4.42856031
4.42856031
4.23333761
说明
子任务可能出现如下的特殊性质:

“西瓜树”退化为一条链

输入数据中的实数均保留8位小数,选手的答案被判作正确当且仅当输出与标准答案误差不超过10^-7。请特别注意浮点数精度问题。
实际数据中,冷气的寒冷程度x的范围为 [-0.1,0.1]
(样例中的冷气寒冷程度的范围为[1,5])

-------------------优美的分割线--------------------------

此题模拟是肯定过不了,妥妥Tle

首先思考图是一条链的特殊情况

想象出一条线,从左到右的一条线,就像这样:


我们往线上一点做个修改,那么从这个点一直往右都会修改,并且修改的值累乘边上的权值
如果普通模拟,O(nm),思考一下,因为m是肯定要的,所以将n降级,将n变成logn
那么一个词浮了出来:数据结构
现在主要面临的问题是一段区间修改的值不一样,这个问题是本题的重点,必须解决
设k[u]为根节点到u节点的k值乘积
现在假设每次修改都是从根节点开始的,这个修改的值现在记为delta,那么轮到v节点的时候,修改的值为delta*k[v]
那么实际上修改的是节点u,释放x的冷气,那么这个delta为定值,delta=x/k[u]

这个*k[v]可以提出来,最后输出时乘上去,平时只要累加delta就行了,而这个累加的操作可以用树状数组/线段树解决

接下来是正解

从链的情况上来,现在只是比链复杂了一个链变成了树而已,那么我们仍然想要用上数据结构,怎么办呢
我们祭出最后的武器:dfs序
把树压扁,变成链不就行了么

我们还得考虑k=0的情况,这个我们在dfs遍历求序的时候同时完成,如果出现这种情况,就把子节点与当前节点分离开来,重新计算k值

Code:

/*
4
1 2 1.00000000
2 3 0.00000000
3 4 1.00000101
9
1 1 3.00000000
9 2
9 3
1 2 1.42856031
9 4
9 2
1 3 4.23333333
9 2
9 4
*/
#include <bits/stdc++.h>
#define res register int
#define ll long long
#define ld long double
#define maxn 2 * 100010
using namespace std;

const ld eps = 1e-8;
struct Edge{
	int to, next;
	ld k;
}edge[maxn];
queue <int> Q;
ld tree[maxn], k[maxn];
int n, m, num, tot, In[maxn], Out[maxn], head[maxn], fa[maxn];

inline void add_edge(int x, int y, ld z){ edge[++num].to = y; edge[num].k = z; edge[num].next = head[x]; head[x] = num; }
inline int lowbit(int x){ return x & -x; }
inline void add(int x, ld y){ for (; x <= n; x += lowbit(x)) tree[x] += y; }
inline ld query(int x){ ld s = 0; for (; x; x -= lowbit(x)) s += tree[x]; return s; }

inline void dfs(int u, int pre, ld K){
	fa[u] = pre;
	k[u] = K;
	In[u] = ++tot;
	for (res i = head[u]; i; i = edge[i].next){
		int v = edge[i].to;
		if (v == pre) continue;
		if (fabs(edge[i].k) < eps){
			fa[v] = u;
			Q.push(v);
			continue;
		}
		dfs(v, u, K * edge[i].k);
	}
	Out[u] = tot;
}

int main(){
	scanf("%d", &n);
	for (res i = 1; i < n; ++ i){
		int x, y; ld z;
		scanf("%d%d%Lf", &x, &y, &z);
		add_edge(x, y, z);
		add_edge(y, x, z);
	}
	Q.push(1);
	while (!Q.empty()){
		dfs(Q.front(), fa[Q.front()], 1.0);
		Q.pop();
	}
	scanf("%d", &m);
	for (res i = 1; i <= m; ++ i){
		int tmp;
		scanf("%d", &tmp);
		if (tmp == 1){
			int x; ld y;
			scanf("%d%Lf", &x, &y);
			add(In[x], y / k[x]);
			add(Out[x] + 1, - y / k[x]);
		} else{
			int x;
			scanf("%d", &x);
			printf("%.8Lf\n", query(In[x]) * k[x]);
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/ModestCoder_/article/details/87027358