hdu6039 Gear Up-线段树

题意:
给出n个齿轮的半径和n个齿轮之间的关系(角速度相等或线速度相等),两种操作,第一种操作:修改一个齿轮的半径,第二种操作:给一个齿轮角速度,输出最大的角速度,答案取ln(自然对数)。
Solution:
我们随意画一个图感受一下:
这里写图片描述
(粗边表示角速度相等,细边表示线速度相等,图中维护的是每个点的角速度和 w 1 w_1 w1的关系)
我们可以发现一个规律:如果修改的点的父亲边是线边,那么到他线边所连的儿子的值是不包含这个点的半径的,也就是说,修改这个点的半径不会影响这个点线边所连的儿子,只会影响角边所连的儿子及儿子所在的子树;如果修改的点的父亲边是角边,那么只会影响这个点线边所连的儿子及儿子所在的子树,不影响这个点角边所连的儿子。

归纳一下:
修改一个点的半径r->r’,如果修改的点的父亲边是线边,那么角边所连的儿子及儿子所在的子树 × r r ′ ×\frac {r}{r'} ×rr,如果修改的点的父亲边是角边,那么线边所连的儿子及儿子所在的子树 × r ′ r ×\frac {r'}{r} ×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)]]));
			}
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值