2017多校训练赛第一场 HDU 6039 Gear Up(线段树+并查集)

来源:互联网 发布:互联网软件开发工资 编辑:程序博客网 时间:2024/06/05 00:08

Gear Up

Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 59    Accepted Submission(s): 16


Problem Description

constroy has some gears, each with a radius. Two gears are considered adjacent if they meet one of the following conditions:

1. They share a common edge (i.e. they have equal linear velocity).

2. They share a common shaft (i.e. they have equal angular velocity).

It is guaranteed that no pair of gears meets both of the above conditions.

A series of continuous adjacent gears constitutes a gear path. There is at most one gear path between each two gears.

Now constroy assigns an angular velocity to one of these gears and then asks you to determine the largest angular velocity among them.

sd0061 thinks this problem is too easy, so he replaces some gears and then asks you the question again.

Input

There are multiple test cases (about 30).

For each test case:

The first line contains three integers n,m,q, the number of gears, the number of adjacent pairs and the number of operations. (0m<n105,0q105)

The second line contains n integers, of which the i-th integer represents ri, the radius of the i-th gear. (ri{2λ0λ30})

Each of the next m lines contains three integers a,x,y, the x-th gear and the y-th gear are adjacent in the a-th condition. (a{1,2},1x,yn,xy)

Each of the next q line contains three integers a,x,y, an operation ruled in the following: (a{1,2},1xn,y{2λ0λ30})

a=1 means to replace the x-th gear with another one of radius y.

a=2 means to assign angular velocity y to the x-th gear and then determine the maximum angular velocity.

Output

For each test case, firstly output "Case #x:" in one line (without quotes), where x indicates the case number starting from 1, and then for each operation of a=2, output in one line a real number, the natural logarithm of the maximum angular velocity, with the precision of3 digits.

Sample Input

4 3 41 4 16 21 2 41 2 32 1 41 1 161 2 42 4 41 4 164 3 52 16 4 82 1 21 2 31 1 42 1 41 3 82 1 161 4 12 1 8

Sample Output

Case #1:1.386Case #2:2.7733.4662.773

Source

2017 Multi-University Training Contest - Team 1



        这题其实没有想象中的那么难,只是当时没有时间去想清楚。

        大致题意:给你一些齿轮,有些齿轮是共边(即线速度相同),有些齿轮是共轴(即角速度相同),每个齿轮都有自己的半径而且大小都是2的次方,保证不会出现矛盾,然后总共有两种操作,一是改变某个齿轮的半径,二是询问如果给某一个齿轮一个角速度(大小也是2的次方),所有齿轮中角速度最大的角速度取自然对数后是多少。

        根据物理知识,如果两个齿轮共边,那么有w1r1=w2r2,为了方便计算,我们不妨取log2(题目数据刚好是2的次方,然后log2是系统自带函数)。如此一来,我们就可以计算出每一个节点与某一个参考节点的半径对数之差,到最后我们就可以用logw=logw0+logr0-logr来求结果。然后对于共轴的情况,我们就可一把他们看作一个集合,用并查集缩点维护。

        然后,我们看看修改的操作,首先当然要计算出半径的该变量delta。接着我们就要分情况讨论一下了,如果修改的点与他的父亲的线速度不相同,那么我们需要改变的,只有与这个改变点共线的齿轮,这个容易理解,因为改变半径的同时并不影响角速度,所以与其共轴的点并没有被影响。然后那些与改变点共线的点与根的半径差都要改变delta。另一种情况就比较复杂了,如果修改的点与父亲的线速度相同,那么所有与该点共角速度的点以及他们的后代与根的半径差都要减少delta。这个如何理解呢?首先,我们看所有与该点共轴的点,我们知道他们的线速度与父亲不同,是通过角速度来与父亲关联的,现在改变点半径改变delta,那么相应角速度就要改变-delta,体现到共轴点身上就是与父亲半径差改变-delta,相当于与根半径差该便-delta。连锁下去就又出现第一种情况,共轴的点半径差改变,那么其后代与之共线的点也要变。改变子树的值的话我们就用线段树,对树的dfs序建立线段树,维护区间更新和区间最大值即可。

        如此一来,我们就完成了比较复杂的修改操作。然后对于询问操作相对来说简单一些,给定角速度的log与当前半径差的差再加上整棵树中最大的半径差,结果就是最大的角速度的log,最后乘上一个log(2),就是我们想要的最大角速度的自然对数。具体见代码吧:

#include<bits/stdc++.h>#define N 101000using namespace std;int n,m,q,cnt,l[N],r[N],ll[N],rr[N],rt[N];typedef pair<int,int> P;typedef pair<P,int> PP;vector<int> g[N];vector<PP> gg[N];int d[N],rad[N];bool v[N];struct ST{struct node{int max,lazy;int l,r;} tree[N<<2];inline void push_up(int i){tree[i].max=max(tree[i<<1].max,tree[i<<1|1].max);}inline void build(int i,int l,int r){tree[i].r=r;tree[i].l=l;tree[i].lazy=0;if (l==r){tree[i].max=d[l];return;}int mid=(l+r)>>1;build(i<<1,l,mid);build(i<<1|1,mid+1,r);push_up(i);}inline void push_down(int i){tree[i<<1].lazy+=tree[i].lazy;tree[i<<1|1].lazy+=tree[i].lazy;tree[i<<1].max+=tree[i].lazy;tree[i<<1|1].max+=tree[i].lazy;tree[i].lazy=0;}inline void update(int i,int l,int r,int x){if ((tree[i].l==l)&&(tree[i].r==r)){tree[i].lazy+=x;tree[i].max+=x;return;}if (tree[i].lazy!=0) push_down(i);int mid=(tree[i].l+tree[i].r)>>1;if (mid>=r) update(i<<1,l,r,x);else if (mid<l) update(i<<1|1,l,r,x);else{update(i<<1,l,mid,x);update(i<<1|1,mid+1,r,x);}push_up(i);}inline int getmax(int i,int l,int r){if ((tree[i].l==l)&&(tree[i].r==r)) return tree[i].max;if (tree[i].lazy!=0) push_down(i);int mid=(tree[i].l+tree[i].r)>>1;if (mid>=r) return getmax(i<<1,l,r);else if (mid<l) return getmax(i<<1|1,l,r);else return max(getmax(i<<1,l,mid),getmax(i<<1|1,mid+1,r));}} seg;int f[N];int find(int x){return f[x]==x ? x:(f[x]=find(f[x]));}void dfs(int root,int fa,int x,int dist){    rt[x]=root;    l[x]=++cnt;//l表示某一个角速度相同的集合以及它所有后代的dfs序的左端点    d[cnt]=dist;    for(int i=0;i<gg[x].size();i++)    {        int w=gg[x][i].second;        int y=gg[x][i].first.first;        int u=gg[x][i].first.second;        if (y!=fa)        {            ll[u]=min(ll[u],cnt+1);//ll表示某一个节点的子树dfs序的左端点            dfs(root,x,y,dist+w);            rr[u]=max(rr[u],cnt);//rr表示某一个节点的子树dfs序的右断点        } else v[u]=1;//v表示该点是否与父亲共线    }    r[x]=cnt;//r表示某一个角速度相同的集合以及它所有后代的dfs序的左端点}int main(){    int T_T=0;    while(~scanf("%d%d%d",&n,&m,&q))    {        cnt=0;        for(int i=1;i<=n;i++)        {            ll[i]=0x7fffffff;            scanf("%d",&rad[i]);            rad[i]=log2(rad[i]);            rt[i]=rr[i]=v[i]=0; f[i]=i;            g[i].clear(); gg[i].clear();        }        while(m--)        {            int ty,u,v;            scanf("%d%d%d",&ty,&u,&v);            if (ty==1)            {                g[u].push_back(v);                g[v].push_back(u);            } else f[find(u)]=find(v);//共轴则合并        }        for(int i=1;i<=n;i++)            for(int j=0;j<g[i].size();j++)            {                int y=g[i][j];                gg[find(i)].push_back(PP(P(find(y),i),rad[i]-rad[y]));//以集合来构建新的树            }        for(int i=1;i<=n;i++)            if (i==find(i)&&!rt[i]) dfs(i,0,i,0);//标dfs序,可能是多棵树,但不影响线段树本身        seg.build(1,1,cnt);        printf("Case #%d:\n",++T_T);        while(q--)        {            int op,x,y;            scanf("%d%d%d",&op,&x,&y);            y=log2(y);            if (op==1)            {                int delta=y-rad[x];                int f=find(x);                if (v[x]) seg.update(1,l[f],r[f],-delta);//如果与父亲共线,先改变整个集合及其后代                if (ll[x]<=rr[x]) seg.update(1,ll[x],rr[x],delta);//如果不共线,只需改变节点子树;共线则子树不用改变,加回去抵消刚刚减少的                rad[x]=y;            } else            {                x=find(x);                int ans=y-seg.getmax(1,l[x],l[x])+seg.getmax(1,l[rt[x]],r[rt[x]]);//结算结果                printf("%.3f\n",ans*log(2.));//记得乘ln2            }        }    }    return 0;}

原创粉丝点击