题解 CF593D 【Happy Tree Party】
看到这个题目,由于不是很会树链剖分、LCT等高级算法,于是考虑用LCA做这个题目
思路很简单,对于每一次查询,直接找到两点的LCA,由于边权很大而且要动态改边,所以两个点要一步一步跳到LCA,在这个过程中将
然后就妥妥的TLE了。。。
此时仔细观察题面,可以发现一些有趣的性质:
虽然边权范围给的很大,但是初始值也只有
这样的话,就可以想到用并查集优化这个做法:把边权为1的边的目标节点记入它父亲节点的并查集,然后查询时两个点往LCA跳的过程中稍作修改,并且当
具体细节见代码
#include<iostream>
#include<cstdio>
#include<cctype>
#define E puts("E?")
#define ll long long
using namespace std;
inline ll read(){
ll w=0,s=1;
char c=getchar();
while(!isdigit(c)){
s=(c=='-')?-1:1;
c=getchar();
}
while(isdigit(c)){
w=(w<<3)+(w<<1)+c-'0';
c=getchar();
}
return w*s;
}
const int N=200005;
ll pre[N],cnt;
struct Edge{
ll nxt,to,wei,from;//链式前向星多记录一个from方便改边权
};
Edge edge[N<<1];
void add_for(ll u,ll v,ll w){
edge[++cnt].nxt=pre[u];
edge[cnt].to=v;
edge[cnt].wei=w;
edge[cnt].from=u;
pre[u]=cnt;
}
ll f[N][21],dep[N],fa[N],va[N];
ll find(ll x){
return fa[x]==x?x:fa[x]=find(fa[x]);//并查集
}
void search(ll u,ll grt){
f[u][0]=grt;
dep[u]=dep[grt]+1;
for(ll i=1;i<=17;i++){
f[u][i]=f[f[u][i-1]][i-1];
}
for(ll i=pre[u];i>0;i=edge[i].nxt){
ll v=edge[i].to,w=edge[i].wei;
if(v!=grt){
if(w==1){
fa[v]=find(u);//在做LCA初始化的时候,遇到边权为1的边就可以把目标节点合并到父亲节点了
}
va[v]=w;//并查集的权值
search(v,u);
}
}
}
ll LCA(ll u,ll v){
if(dep[u]<dep[v]){
swap(u,v);
}
for(ll i=17;i>=0;i--){
if(dep[f[u][i]]>=dep[v]){
u=f[u][i];
}
if(u==v){
return u;
}
}
for(ll i=17;i>=0;i--){
if(f[u][i]!=f[v][i]){
u=f[u][i];
v=f[v][i];
}
}
return f[u][0];
}
ll n,q;
int main(){
n=read();
q=read();
ll u,v,w;
for(ll i=1;i<n;i++){
u=read();
v=read();
w=read();
add_for(u,v,w);
add_for(v,u,w);//无向图连两条边
}
for(ll i=1;i<=n;i++){
fa[i]=i;//并查集初始化
}
search(1,0);
ll opt,x,y,z;
for(ll p=1;p<=q;p++){
opt=read();
if(opt==1){//查询操作
x=read();
y=read();
z=read();
ll anc=LCA(x,y),a=x,b=y;
while(dep[a]>dep[anc]&&z){//"&&z"就是当v为0时可以不用再跳了
if(va[a]){
z/=va[a];
}
a=find(f[a][0]);//借助并查集,每次上跳不是简单的跳到父亲,而是父亲所在并查集的祖先(?)
}
while(dep[b]>dep[anc]&&z){
if(va[b]){
z/=va[b];
}
b=find(f[b][0]);
}
printf("%lld\n",z);
}
else{//修改操作
x=read();
y=read();
ll u=edge[x<<1].from,v=edge[x<<1].to;
if(f[u][0]==v){
z=u;//由于我们做并查集的时候是把儿子并入父亲,所以改边时要看from和to哪个在树上是父亲
}
else{
z=v;
}
va[z]=y;
if(va[z]==1){
fa[z]=find(f[z][0]);//边权为1就合并
}
}
}
return 0;
}
GL~