前言
由于之前没有写过这类博客,特地补一篇
题目
讲解
如果你做过这道题: 上帝造题的七分钟2 / 花神游历各国(洛谷)
那么可能对你做这道题有帮助
我们发现,对于询问,如果除数不等于(1),那么在log级别的次数内,(y_i)即可被除为(0)
有了这个发现,我们就很容易求得答案
对于一次询问,暴力找出这条路径上所有不等于1的边,一旦统计数超过(60(log_2(1e18)approx 60))
这时候就会有人问了:“如果暴力爬树的时候全都是1的边不就TLE了吗?”
诶,我们用一个并查集就可以保证时间复杂度了,如果边为1,我们直接把它在并查集中连到祖先去
这样在下次就不会经过它了,从而保证时间复杂度
代码
int head[MAXN],tot;
struct edge
{
int v,ID,nxt;
LL w;
}e[MAXN << 1];
void Add_Edge(int x,int y,LL z,int ID)
{
e[++tot].v = y;
e[tot].w = z;
e[tot].ID = ID;
e[tot].nxt = head[x];
head[x] = tot;
}
void Add_Double_Edge(int x,int y,LL z,int ID)
{
Add_Edge(x,y,z,ID);
Add_Edge(y,x,z,ID);
}
int f[MAXN][18],d[MAXN],eID[MAXN],fa[MAXN],bcj[MAXN];
LL w[MAXN];
int findSet(int x)
{
if(bcj[x] != x) bcj[x] = findSet(bcj[x]);
return bcj[x];
}
void dfs(int x,int FA)
{
d[x] = d[FA] + 1;
f[x][0] = FA;
for(int i = 1;i <= 17;++ i) f[x][i] = f[f[x][i-1]][i-1];
for(int i = head[x]; i ;i = e[i].nxt)
if(!d[e[i].v])
eID[e[i].ID] = e[i].v,w[e[i].v] = e[i].w,dfs(e[i].v,x);
}
int lca(int x,int y)
{
if(d[x] < d[y]) swap(x,y);
for(int i = 17;i >= 0;-- i) if(d[f[x][i]] >= d[y]) x = f[x][i];
if(x == y) return x;
for(int i = 17;i >= 0;-- i)
if(f[x][i] != f[y][i])
x = f[x][i],y = f[y][i];
return f[x][0];
}
LL a[65],b[65];
void Get(int x,int LCA,LL *z,int &cnt)
{
while(d[findSet(x)] > d[LCA] && cnt < 60)
{
x = findSet(x);
if(w[x] == 1) bcj[x] = findSet(f[x][0]);
else z[++cnt] = w[x];
x = f[x][0];
}
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n = Read(); m = Read();
for(int i = 1;i < n;++ i)
{
int u = Read(),v = Read();
Add_Double_Edge(u,v,Read(),i);
}
for(int i = 1;i <= n;++ i) bcj[i] = i;
dfs(1,0);
while(m--)
{
int opt = Read();
if(opt == 1)
{
int u = Read(),v = Read(),cnta = 0,cntb = 0;
LL val = Read();
int LCA = lca(u,v);
Get(u,LCA,a,cnta);
Get(v,LCA,b,cntb);
if(cnta + cntb >= 60) Put(0,'
');
else
{
for(int i = 1;i <= cnta && val;++ i) val /= a[i];
for(int i = cntb;i >= 1 && val;-- i) val /= b[i];
Put(val,'
');
}
}
else
{
int p = Read();
w[eID[p]] = Read();
}
}
return 0;
}