[CF593D]Happy Tree Party

题目

传送门 to luogu

思路壹

首先我要告诉你,这可以变成一个树剖的板子题,因为

n a b = n a b \left\lfloor\frac{\lfloor\frac{n}{a}\rfloor}{b}\right\rfloor=\left\lfloor\frac{n}{ab}\right\rfloor

这是容易说明的。考虑左式,其数学意义为,找一最大整数 x x 使得 b x n a bx\le\lfloor\frac{n}{a}\rfloor ,显然等价于 b x n a bx\le \frac{n}{a} ,自然也等价于 a b x n abx\le n ,与右式的数学意义相同。

然后我们只需要维护树上路径的乘积。复杂度 O ( m log 2 n + n log n ) \mathcal O(m\log^2n+n\log n)

代码壹

#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
typedef long long int_;
inline int_ readint(){
	int_ a = 0; char c = getchar(), f = 1;
	for(; c<'0'||c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c&&c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

const int MaxN = 200005;
int n;

struct Edge{
	int to, nxt; int_ val;
	Edge(){ } // nothing
	Edge(int T,int N,int_ V){
		to = T, nxt = N, val = V;
	}
} e[MaxN<<1|1];
int head[MaxN], cntEdge;
void addEdge(int a,int b,int_ c){
	e[cntEdge] = Edge(b,head[a],c);
	head[a] = cntEdge ++;
	e[cntEdge] = Edge(a,head[b],c);
	head[b] = cntEdge ++;
}

int siz[MaxN], son[MaxN], fa[MaxN];
void dfs(int x){
	siz[x] = 1;
	for(int i=head[x]; ~i; i=e[i].nxt)
		if(e[i].to != fa[x]){
			fa[e[i].to] = x, dfs(e[i].to);
			siz[x] += siz[e[i].to];
			if(siz[e[i].to] > siz[son[x]])
				son[x] = e[i].to;
		}
}

void modify(int,int_,int,int);
int top[MaxN], dfn[MaxN], dfsClock;
void build(int x,int tc){
	top[x] = tc, dfn[x] = ++ dfsClock;
	if(son[x]) build(son[x],tc);
	for(int i=head[x]; ~i; i=e[i].nxt)
		if(e[i].to != fa[x]){
			if(e[i].to != son[x])
				build(e[i].to,e[i].to);
			modify(dfn[e[i].to],e[i].val,1,n);
		}
}

const int_ __M = 1ll<<60;
int_ mul(int_ a,int_ b){
	return __M/a < b ? __M : a*b;
}

int_ v[MaxN<<1]; // 线段树节点
int __id(int l,int r){ return (l+r)|(l!=r); }
void modify(int qid,int_ adv,int l=1,int r=n){
	if(l == r){
		v[l<<1] = adv; return ;
	}
	int m = (l+r)>>1;
	if(qid <= m) modify(qid,adv,l,m);
	else modify(qid,adv,m+1,r);
	v[m<<1|1] = mul(v[__id(l,m)],v[__id(m+1,r)]);
}
int_ query(int ql,int qr,int l,int r=n){
	if(ql <= l && r <= qr)
		return v[__id(l,r)];
	int_ res = 1; int m = (l+r)>>1;
	if(ql <= m)
		res = mul(res,query(ql,qr,l,m));
	if(m < qr)
		res = mul(res,query(ql,qr,m+1,r));
	return res;
}

int_ query(int a,int b){
	int_ res = 1;
	while(top[a] != top[b]){
		if(dfn[top[a]] < dfn[top[b]]) swap(a,b);
		res = mul(res,query(dfn[top[a]],dfn[a],1));
		a = fa[top[a]];
	}
	if(a == b) return res;
	if(dfn[a] < dfn[b]) swap(a,b);
	return mul(res,query(dfn[b]+1,dfn[a],1));
}

int main(){
	n = readint(); int m = readint();
	for(int i=1; i<=n; ++i)
		head[i] = -1;
	for(int i=2; i<=n*2; ++i)
		v[i] = 1; // 防止 mul 的时候 RE
	for(int i=1,a,b; i<n; ++i){
		a = readint(), b = readint();
		addEdge(a,b,readint());
	}
	dfs(1), build(1,1);
	while(m --){
		if(readint() == 2){
			int a = readint()-1;
			int b = e[a<<1].to;
			a = e[a<<1|1].to;
			if(dfn[a] < dfn[b])
				swap(a,b);
			modify(dfn[a],readint());
			continue;
		}
		int_ dym = query(readint(),readint());
		printf("%lld\n",readint()/dym);
	}
	return 0;
}

思路贰

有另一个很神奇的想法。作除法的下降速度极快,是 O ( log v ) \mathcal O(\log v) 级别的。唯一麻烦的是权值为 1 1 的边。

嘿,现在想想,题目中的修改只会让权值变小,也就是说,变成 1 1 就不会变回去。于是用并查集快速查找第一条不为 1 1 的边。

复杂度 O ( n log n + m log v ) \mathcal O(n\log n+m\log v) ,跑起来比树剖快一些。注意:树上并查集不可以用按秩合并,所以复杂度是最坏 O ( n log n ) \mathcal O(n\log n) 的。

代码贰

#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
typedef long long int_;
inline int_ readint(){
	int_ a = 0; char c = getchar(), f = 1;
	for(; c<'0'||c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c&&c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

const int MaxN = 200005;

struct Edge{
	int to, nxt; int_ val;
	Edge(){ } // nothing
	Edge(int T,int N,int_ V){
		to = T, nxt = N, val = V;
	}
} e[MaxN<<1|1];
int head[MaxN], cntEdge;
void addEdge(int a,int b,int_ c){
	e[cntEdge] = Edge(b,head[a],c);
	head[a] = cntEdge ++;
	e[cntEdge] = Edge(a,head[b],c);
	head[b] = cntEdge ++;
}

namespace UFS{
	int fa[MaxN];
	void init(int n){
		for(int i=1; i<=n; ++i)
			fa[i] = i;
	}
	inline int find(int a){
		if(a != fa[a])
			fa[a] = find(fa[a]);
		return fa[a];
	}
	void combine(int a,int b){
		fa[find(a)] = find(b);
	}
}

int dep[MaxN], fa[MaxN];
int_ fae[MaxN]; // 其实也可以改成 depth
void build(int x){
	dep[x] = dep[fa[x]]+1;
	for(int i=head[x]; ~i; i=e[i].nxt){
		if(e[i].to == fa[x]) continue;
		fa[e[i].to] = x;
		fae[e[i].to] = e[i].val;
		build(e[i].to);
		if(e[i].val == 1)
			UFS::combine(e[i].to,x);
	}
}

int_ query(int a,int b,int_ c){
	a = UFS::find(a), b = UFS::find(b);
	while(a != b){
		if(dep[a] < dep[b]) swap(a,b);
		c /= fae[a], a = UFS::find(fa[a]);
		if(c == 0) break; // 重要!
	}
	return c;
}

int main(){
	int n = readint(), m = readint();
	for(int i=1; i<=n; ++i)
		head[i] = -1;
	UFS::init(n);
	for(int i=1,a,b; i<n; ++i){
		a = readint(), b = readint();
		addEdge(a,b,readint());
	}
	build(1);
	while(m --){
		if(readint() == 2){
			int a = readint()-1;
			int b = e[a<<1].to;
			a = e[a<<1|1].to;
			if(dep[a] < dep[b])
				swap(a,b);
			fae[a] = readint();
			if(fae[a] == 1)
				UFS::combine(a,b);
			continue;
		}
		int a = readint(), b = readint();
		printf("%lld\n",query(a,b,readint()));
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42101694/article/details/108111964