Description
有n个齿轮,每个齿轮有一个半径rad[i],齿轮间有两种关系:共轴(角速度相同)或共边(线速度相同),保持两个齿轮之间不会同时出现这两种关系,且任意两个齿轮之间只有一条路径,有两种操作:
1 x y:把x齿轮的半径变成y
2 x y:给x齿轮角速度y,求所有齿轮中最大的角速度的自然对数值
Input
多组用例,每组用例首先输入三个整数n,m,q分别表示齿轮数,齿轮之间的关系数以及操作数,之后n个整数rad[i]表示第i个齿轮的半径,然后m行每行输入三个整数type x y表示x齿轮和y齿轮的关系,type=1表示两个齿轮共线速度,type=2表示两个齿轮共角速度,最后q行每行三个整数type x y表示一个操作,type=1表示把x齿轮的半径变成y,type=2表示给x齿轮角速度y,求所有齿轮中最大的角速度的自然对数值,以文件尾结束输入(0<=m < n<=1e5,0<=q<=1e5,rad[i],y=2^t(0<=t<=30))
Output
对于每次查询操作,输出一个答案,结果保留到小数点后三位
Sample Input
4 3 4
1 4 16 2
1 2 4
1 2 3
2 1 4
1 1 16
1 2 4
2 4 4
1 4 16
4 3 5
2 16 4 8
2 1 2
1 2 3
1 1 4
2 1 4
1 3 8
2 1 16
1 4 1
2 1 8
Sample Output
Case #1:
1.386
Case #2:
2.773
3.466
2.773
Solution
并查集维护共轴的点,在共线的点所属集合之间建边构成一个森林(该森林的每个节点其实是一个由若干共轴点组成的集合),一个集合中的点有两种,第一种是与父亲节点共线的(即该集合连向父亲集合是由于这个点与父亲集合中某点共线),另一种与父亲节点不共线
选取根集合中某点作为基准点,设其角速度为定值,x节点与y节点线速度相同即可得到关系w[x]*rad[x]=w[y]*rad[y],由于rad和w都是2的幂次,不妨先取log2,则有w[x]+rad[x]=w[y]+rad[y],那么只要我们知道基准点的角速度以及基准点与任意节点的半径差,即可得到任意节点的角速度,故求出dfs序把这个森林转化为连续区间,用线段树维护基准接点与每一点的半径差
对于更新操作,如果更新的节点x与父亲节点不共线,那么由于其所属集合的角速度不变,x的角速度也不变,但是对于与该点共线的点y,由于w[x]+rad[x]=w[y]+rad[y],w[x]不变,rad[x]增加了d=y-rad[x],那么w[y]也增加了d,反映到半径差上就是基准点与y点的半径差增加d;如果x与附近节点共线,由于x的半径增加了d,那么x的角速度相应的就减少d,进而与x共轴的所有点角速度均减少d,进而与这些点共线的点的角速度也减少d,故以x所在集合为根的子树中所有节点的角速度均减少d,反映到半径差上就是基准点与这些点的半径差减少d
对于查询操作,给出x点的角速度,由基准点与x点的半径差即可得到基准点的角速度,基准点的角速度加上以基准点为根节点的子树中选出半径差最大值即为所有点的角速度最大值,该最大值是log2意义下的,答案要求的是自然对数意义下的,所以把答案乘一个In(2)即可
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const int INF=0x3f3f3f3f,maxn=100001;
int n,m,q;
struct node
{
int v,w,d;
};
vector<int>g[maxn];//g存共线齿轮的关系
vector<node>gg[maxn];//gg存并查集缩完共轴之轮后的树
int rad[maxn],d[maxn];//rad[i]表示第i个齿轮半径取完log2后的值,d[i]表示根齿轮与第i个齿轮rad之差
int index,L[maxn],R[maxn],l[maxn],r[maxn],rt[maxn];
//index时间戳,[L[i],R[i]]是缩完点后的i点的子树dfs序构成的区间
//[l[i],r[i]]是缩点之前i点在缩完点后的树中子树dfs序构成的区间
//缩点后的点的[L,R]区间即为这个点集中点的[l,r]的并
int f[maxn];//并查集,维护共轴的点
int vis[maxn];//vis[i]=1表示i点是i点将其所在集合与父亲集合相连,即i点与其所在集合的父亲集合中某点共线
int find(int x)
{
if(f[x]==x)return x;
return f[x]=find(f[x]);
}
#define ls (t<<1)
#define rs ((t<<1)|1)
int Max[maxn<<2],Lazy[maxn<<2];
void push_up(int t)
{
Max[t]=max(Max[ls],Max[rs]);
}
void build(int l,int r,int t)
{
Lazy[t]=0;
if(l==r)
{
Max[t]=d[l];
return ;
}
int mid=(l+r)/2;
build(l,mid,ls),build(mid+1,r,rs);
push_up(t);
}
void push_down(int t)
{
if(Lazy[t])
{
Lazy[ls]+=Lazy[t],Max[ls]+=Lazy[t];
Lazy[rs]+=Lazy[t],Max[rs]+=Lazy[t];
Lazy[t]=0;
}
}
void update(int L,int R,int l,int r,int t,int v)
{
if(L<=l&&r<=R)
{
Lazy[t]+=v,Max[t]+=v;
return ;
}
push_down(t);
int mid=(l+r)/2;
if(L<=mid)update(L,R,l,mid,ls,v);
if(R>mid)update(L,R,mid+1,r,rs,v);
push_up(t);
}
int query(int L,int R,int l,int r,int t)
{
if(L<=l&&r<=R)return Max[t];
push_down(t);
int mid=(l+r)/2,ans=-INF;
if(L<=mid)ans=max(ans,query(L,R,l,mid,ls));
if(R>mid)ans=max(ans,query(L,R,mid+1,r,rs));
return ans;
}
void dfs(int u,int fa,int root,int dis)
{
rt[u]=root,L[u]=++index,d[index]=dis;
for(int i=0;i<gg[u].size();i++)
{
int v=gg[u][i].v,w=gg[u][i].w,d=gg[u][i].d;
if(v!=fa)
{
l[w]=min(l[w],index+1);
dfs(v,u,root,dis+d);
r[w]=max(r[w],index);
}
else vis[w]=1;
}
R[u]=index;
}
int main()
{
int Case=1;
while(~scanf("%d%d%d",&n,&m,&q))
{
index=0;
for(int i=1;i<=n;i++)
{
l[i]=INF,r[i]=0,rt[i]=0,vis[i]=0,f[i]=i;
g[i].clear(),gg[i].clear();
scanf("%d",&rad[i]);
rad[i]=log2(rad[i]);
}
while(m--)
{
int op,u,v;
scanf("%d%d%d",&op,&u,&v);
if(op==1)g[u].push_back(v),g[v].push_back(u);
else f[find(u)]=find(v);
}
for(int u=1;u<=n;u++)
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
gg[find(u)].push_back((node){find(v),u,rad[u]-rad[v]});
}
for(int i=1;i<=n;i++)
if(find(i)==i&&!rt[i])dfs(i,0,i,0);
build(1,index,1);
printf("Case #%d:\n",Case++);
while(q--)
{
int op,x,y;
scanf("%d%d%d",&op,&x,&y);
y=log2(y);
if(op==1)
{
int change=y-rad[x],fa=find(x);
if(vis[x])update(L[fa],R[fa],1,index,1,-change);
if(l[x]<=r[x])update(l[x],r[x],1,index,1,change);
rad[x]=y;
}
else
{
x=find(x);
int ans=y-query(L[x],L[x],1,index,1)+query(L[rt[x]],R[rt[x]],1,index,1);
printf("%.3f\n",log(2.0)*ans);
}
}
}
return 0;
}