题意:
给出n个齿轮的半径和n个齿轮之间的关系(角速度相等或线速度相等),两种操作,第一种操作:修改一个齿轮的半径,第二种操作:给一个齿轮角速度,输出最大的角速度,答案取ln(自然对数)。
Solution:
我们随意画一个图感受一下:

(粗边表示角速度相等,细边表示线速度相等,图中维护的是每个点的角速度和 w1 的关系)
我们可以发现一个规律:如果修改的点的父亲边是线边,那么到他线边所连的儿子的值是不包含这个点的半径的,也就是说,修改这个点的半径不会影响这个点线边所连的儿子,只会影响角边所连的儿子及儿子所在的子树;如果修改的点的父亲边是角边,那么只会影响这个点线边所连的儿子及儿子所在的子树,不影响这个点角边所连的儿子。

归纳一下:
修改一个点的半径r->r’,如果修改的点的父亲边是线边,那么角边所连的儿子及儿子所在的子树 ×rr ,如果修改的点的父亲边是角边,那么线边所连的儿子及儿子所在的子树 ×rr
那么这道题就可以用线段树搞了,求出这棵树的dfs序,维护区间乘,区间取max即可,因为这道题要求取自然对数,所以我们可以维护ln值,就可以把区间乘转化为区间加了。

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
const int N=100010;
int n,m;
int size,head[N];
struct edg{
    int from,to,next;
    double v;
}g[2*N],e[2*N];
int f[N];
int q;
int h[N],l[N],r[N],ll[N],rr[N];
double ra[N];
int fa[N];
int T,x,y,a,rt[N],cnt;
double v[N];
bool p[N];
struct tree{
    int l,r;
    double tag,maxn;
}tr[4*N];
void update(int i)
{
    tr[i].maxn=max(tr[i<<1].maxn,tr[i<<1|1].maxn);
    return;
}
void build(int i,int l,int r)
{
    tr[i].l=l;tr[i].r=r;
    tr[i].maxn=0;tr[i].tag=0;
    if (l==r) {
  tr[i].maxn=v[l];return;}
    int mid=(l+r)>>1;
    build(i<<1,l,mid);
    build(i<<1|1,mid+1,r);
    update(i);
}
void addtag(int i,double x)
{
    tr[i].tag+=x;
    tr[i].maxn+=x;
}
void pushdown(int i)
{
    if (tr[i].tag)
    {
        if (tr[i].l==tr[i].r) return;
        addtag(i<<1,tr[i].tag);
        addtag(i<<1|1,tr[i].tag);
        tr[i].tag=0;
    }
}
void modify(int i,int l,int r,double x)
{
    int L=tr[i].l,R=tr[i].r;
    if (l>R||r<L) return;
    if (l<=L&&R<=r) {addtag(i,x);return;}
    pushdown(i);
    modify(i<<1,l,r,x);
    modify(i<<1|1,l,r,x);
    update(i);
}
double query(int i,int l,int r)
{
    int L=tr[i].l,R=tr[i].r;
    if (l>R||r<L) return -1e9;
    if (l<=L&&R<=r) return tr[i].maxn;
    pushdown(i);
    double ans=-1e9;
    ans=max(ans,query(i<<1,l,r));
    ans=max(ans,query(i<<1|1,l,r));
    return ans;
}
int find(int x)
{
    if (fa[x]!=x) fa[x]=find(fa[x]);
    return fa[x];
}
void add(int x,int y)
{
    size++;
    e[size].to=y;
    e[size].next=head[x];
    head[x]=size;
}
void ad(int x,int y,int f,double v)
{
    size++;
    g[size].to=y;
    g[size].v=v;
    g[size].from=f;
    g[size].next=h[x];
    h[x]=size;
}
void dfs(int root,int fa,int x,double dis)
{
    rt[x]=root;
    l[x]=++cnt;
    v[cnt]=dis;
    for (int i=h[x];i;i=g[i].next)
    {
        int y=g[i].to;
        int xx=g[i].from;
        if (y!=fa)
        {
            ll[xx]=min(ll[xx],cnt+1);
            dfs(root,x,y,1.0*dis+g[i].v);
            rr[xx]=max(rr[xx],cnt);
        }
        else p[xx]=1;
    }
    r[x]=cnt;
}
int main()
{
    while (scanf("%d%d%d",&n,&m,&q)!=EOF)
    {
        memset(p,0,sizeof(p));
        memset(h,0,sizeof(h));
        memset(head,0,sizeof(head));
        memset(rt,0,sizeof(rt)); 
        for (int i=1;i<=n;i++)
            scanf("%lf",&ra[i]),ra[i]=log(ra[i]),fa[i]=i;
        size=0;
        for (int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&a,&x,&y);
            if (a==1) add(x,y),add(y,x);
            else x=find(x),y=find(y),fa[x]=y;
        }
        size=0;
        for (int i=1;i<=n;i++)
        {
            ll[i]=1e9,rr[i]=0;
            for (int j=head[i];j;j=e[j].next)
            {
                int x=e[j].to;
                ad(find(i),find(x),i,ra[i]-ra[x]);
            }
        }
        cnt=0;
        for (int i=1;i<=n;i++)if (find(i)==i&&!rt[i]) dfs(i,0,i,0.0);
        build(1,1,cnt);

        printf("Case #%d:\n",++T);
        for (int i=1;i<=q;i++)
        {
            scanf("%d%d%d",&a,&x,&y);
            double yy=log(y);
            if (a==1)
            {
                if (p[x]) modify(1,l[find(x)],r[find(x)],-yy+ra[x]);
                if (ll[x]<=rr[x]) modify(1,ll[x],rr[x],yy-ra[x]);
                ra[x]=yy;
            }
            else
            {
                printf("%.3f\n",yy-query(1,l[find(x)],l[find(x)])+query(1,l[rt[find(x)]],r[rt[find(x)]]));
            }
        }
    }
}