codeforces 593D happy tree party 树链剖分

/*
	题目描述:给定一棵树及其边权,有两种操作
			  1 u v y 用y依次去除uv的公共路径上的每一条边的边权,注意这里的除法是整数的除法
			  2 x p 把第x条输入边的边权变为p

	方法:容易证明,依次去除每一条边权,等于除以这条路径上所有边权的乘积,原因是前者相当于除一次保留一次,后者相当于
           每次除完保留小数,最后保留一次,根据这个理论可以证明二者结果相同。
	   使用树链剖分来处理,维护区间乘积,用long double存,超过1e18就维护成1e18 + 100
*/
#pragma warning(disable:4786)
#pragma comment(linker, "/STACK:102400000,102400000")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<cmath>
#include<string>
#include<sstream>
#define LL long long
#define LD long double
#define FOR(i,f_start,f_end) for(int i=f_start;i<=f_end;++i)
#define mem(a,x) memset(a,x,sizeof(a))
#define lson l,m,x<<1
#define rson m+1,r,x<<1|1
#define ebp  1000000000000000100LL
#define eb 1000000000000000000.000
using namespace std;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const double PI = acos(-1.0);
const double eps = 1e-6;

using namespace std;

const int maxn = 2e5 + 5;
struct Edge
{
	int to, next;
}edge[maxn * 2];
int head[maxn], tot;
int top[maxn];//top[v]表示v所在的重链的顶端节点
int fa[maxn]; //父亲节点
int deep[maxn];//深度
int num[maxn];//num[v]表示以v为根的子树的节点数
int p[maxn];//p[v]表示v与其父亲节点的连边在线段树中的位置
int fp[maxn];//和p数组相反
int son[maxn];//重儿子
int from[maxn], to[maxn];
LD t[4 * maxn], w[maxn];
int pos, n;
void init()
{
	tot = 0;
	memset(head, -1, sizeof(head));
	pos = 1;
	memset(son, -1, sizeof(son));
}
void addedge(int u, int v)
{
	edge[tot].to = v; edge[tot].next = head[u]; head[u] = tot++;
}
void dfs1(int u, int pre, int d) //第一遍dfs求出fa,deep,num,son
{
	deep[u] = d;
	fa[u] = pre;
	num[u] = 1;
	for (int i = head[u]; i != -1; i = edge[i].next)
	{
		int v = edge[i].to;
		if (v != pre)
		{
			dfs1(v, u, d + 1);
			num[u] += num[v];
			if (son[u] == -1 || num[v] > num[son[u]])
				son[u] = v;
		}
	}
}
void getpos(int u, int sp) //第二遍dfs求出top和p
{
	top[u] = sp;
	if (son[u] != -1)
	{
		p[u] = pos++;
		fp[p[u]] = u;
		getpos(son[u], sp);
	}
	else
	{
		p[u] = pos++;
		fp[p[u]] = u;
		return;
	}
	for (int i = head[u]; i != -1; i = edge[i].next)
	{
		int v = edge[i].to;
		if (v != son[u] && v != fa[u])
			getpos(v, v);
	}
}

void pushup(int l, int r, int x)
{
	t[x] = t[x << 1] * t[x << 1 | 1];
	if (t[x] > eb)
		t[x] = eb + 100;
}

void modify(int pos, LD val, int l, int r, int x)
{
	if (l == r && pos == l){
		t[x] = val;
		return;
	}
	int m = l + (r - l) / 2;
	if (pos <= m)
		modify(pos, val, lson);
	else if (pos > m)
		modify(pos, val, rson);
	pushup(l, r, x);
}
LD query(int L, int R, int l, int r, int x)
{
	if (L == l && R == r){
		return t[x];
	}
	int m = l + (r - l) / 2;
	if (R <= m)
		return query(L, R, lson);
	else if (L > m)
		return query(L, R, rson);
	else{
		LD q1 = query(L, m, lson);
		LD q2 = query(m + 1, R, rson);
		LD ret = q1 * q2;
		if (ret > eb)
			return eb + 100;
		else
			return ret;
	}
}
LD find(int u, int v)//查询u->v边的最大值
{
	int f1 = top[u], f2 = top[v];
	LD tmp = 1.0, Q;
	while (f1 != f2)
	{
		if (deep[f1] < deep[f2])
		{
			swap(f1, f2);
			swap(u, v);
		}
		Q = query(p[f1], p[u], 1, n, 1);
		if (tmp <= eb)
			tmp *= Q;
		if (tmp > eb)		tmp = eb + 100;
		u = fa[f1]; f1 = top[u];
	}
	if (u == v)return tmp;
	if (deep[u] > deep[v])   swap(u, v);
	Q = query(p[son[u]], p[v], 1, n, 1);
	tmp *= Q;
	if (tmp > eb)		tmp = eb + 100;
	return tmp;
}

int main()
{
	int m, type, x , y;
	LD v1 , v2;
	init();
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n - 1; i++){
		scanf("%d%d%Lf", &from[i], &to[i], &w[i]);
		addedge(from[i], to[i]);
		addedge(to[i], from[i]);
	}
	mem(t, 0);
	dfs1(1, 1, -1);
	getpos(1, 1);
	for (int i = 1; i <= n - 1; i++){
		if (deep[from[i]] > deep[to[i]])
			swap(from[i], to[i]);
		modify(p[to[i]], w[i], 1, n, 1);
	}
	for (int i = 1; i <= m; i++){
		scanf("%d", &type);
		if (type == 1){
			scanf("%d%d%Lf", &x, &y, &v1);
			LD Q = find(x, y);
			LD res = v1 / Q;
			LL ans = res;
			printf("%lld\n", ans);
		}
		else{
			scanf("%d%Lf", &x, &v2);
			if (deep[from[x]] > deep[to[x]])
				swap(from[x], to[x]);
			modify(p[to[x]], v2, 1, n, 1);
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值