题意:
给出n个齿轮的半径和n个齿轮之间的关系(角速度相等或线速度相等),两种操作,第一种操作:修改一个齿轮的半径,第二种操作:给一个齿轮角速度,输出最大的角速度,答案取ln(自然对数)。
Solution:
我们随意画一个图感受一下:
(粗边表示角速度相等,细边表示线速度相等,图中维护的是每个点的角速度和 w1 的关系)
我们可以发现一个规律:如果修改的点的父亲边是线边,那么到他线边所连的儿子的值是不包含这个点的半径的,也就是说,修改这个点的半径不会影响这个点线边所连的儿子,只会影响角边所连的儿子及儿子所在的子树;如果修改的点的父亲边是角边,那么只会影响这个点线边所连的儿子及儿子所在的子树,不影响这个点角边所连的儿子。
归纳一下:
修改一个点的半径r->r’,如果修改的点的父亲边是线边,那么角边所连的儿子及儿子所在的子树 ×r′r ,如果修改的点的父亲边是角边,那么线边所连的儿子及儿子所在的子树 ×r′r 。
那么这道题就可以用线段树搞了,求出这棵树的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)]]));
}
}
}
}