HDU 6039 Gear Up(2017 Multi-University Training Contest 1)

题目链接:Here

题意:给你一些齿轮以及齿轮之间的关系,然后每次有两种操作:1.把第x个齿轮的半径更改为y;2.给第x个齿轮一个大小为y的角速度,问所有齿轮中最大的角速度的自然对数是多少。

解法:根据齿轮之间的连接关系(共角速度或者共线速度)建图,我们就得到了一片森林。每一棵树任取一个点作为参照点,然后对于操作对象所在的树单独考虑。先讨论操作1,如果被修改的节点的父节点和该节点是共角速度的,那么这个修改操作只会影响这个所有与这个节点共线速度的节点。如果该节点与父节点共线速度,那么操作会影响该节点以及所有与该节点共角速度的点。对于操作2,我们只需要减去这个点相对于参照点的值,然后再加上所在树的最大权值即可。

这里有两个trick:

1.由于每一个齿轮的半径一定是2的幂次,所以可以对半径直接取log2,那么我们每一次操作都变成了整数之间的运算,结果乘上log(2.0)就行,又好写又快~

2.对于每一个节点,把这个节点所有连接的点排序,使得和该点共角速度的点在对应vector中的前缀里,这样我们做修改操作的时候就可以分成两个区间讨论了。

#include <stdio.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 100005;
const double EPS = 1e-8;
struct Node{
    int l,r,v,tag;
};
vector<vector<Node> >root;//记录森林中的每一棵线段树的根节点
int n,m,q,cnt,st,t,siz,bel[N],id[N],sz[N],rsz[N],f[N],r[N],bs[N];
//数组分别代表编号为i的节点:属于第bel[i]棵线段树;在dfs序中的编号;子树大小;与该节点共角速度的节点个数;父节点;半径;与参照点的相对大小
vector<pair<int,bool> >e[N];//bool为真时表示共角速度,反之为共线速度
bool vis[N];

int Log(int k){
    int res=0;
    while(k!=1){
        k/=2;
        res++;
    }
    return res;
}

inline bool cmp(const pair<int,bool>& q,const pair<int,bool>& w){
    if(q.second!=w.second)
        return q.second;
    return q.first>w.first;
}

void dfs(int last,int now,int bias){
    f[now]=(last==-1?now:last);
    id[now]=st++;
    bel[now]=cnt;
    sz[now]=1;
    rsz[now]=1;
    bs[now]=bias;
    for(int i=0;i<e[now].size();i++)
        if(!vis[e[now][i].first]&&e[now][i].first!=last){
            vis[e[now][i].first]=true;
            dfs(now,e[now][i].first,bias+(e[now][i].second?0:r[now]-r[e[now][i].first]));
            sz[now]+=sz[e[now][i].first];
            rsz[now]+=(e[now][i].second?1:0)*sz[e[now][i].first];
        }
}

void Build(int ps,int i,int l,int r){
    root[ps][i].l=l;
    root[ps][i].r=r;
    root[ps][i].tag=root[ps][i].v=0;

    if(l==r)
        return ;

    int mid=l+r>>1;
    Build(ps,i<<1,l,mid);
    Build(ps,i<<1|1,mid+1,r);
}

inline void PushUp(int ps,int i){
    root[ps][i].v=max(root[ps][i<<1].v,root[ps][i<<1|1].v);
}

inline void PushDown(int ps,int i){
    if(root[ps][i].tag){
        root[ps][i<<1].v+=root[ps][i].tag;
        root[ps][i<<1].tag+=root[ps][i].tag;
        root[ps][i<<1|1].v+=root[ps][i].tag;
        root[ps][i<<1|1].tag+=root[ps][i].tag;
        root[ps][i].tag=0;
    }
}

void Ist(int ps,int i,int x,int val){
    if(root[ps][i].l==x&&root[ps][i].r==x){
        root[ps][i].v=val;
        return ;
    }

    if(x<=root[ps][i<<1].r)
        Ist(ps,i<<1,x,val);
    else if(root[ps][i<<1|1].l<=x)
        Ist(ps,i<<1|1,x,val);
    PushUp(ps,i);
}

void Update(int ps,int i,int l,int r,int bias){
    if(l>r)
        return ;
    if(l<=root[ps][i].l&&root[ps][i].r<=r){
        root[ps][i].v+=bias;
        root[ps][i].tag+=bias;
        return ;
    }

    PushDown(ps,i);
    if(l<=root[ps][i<<1].r)
        Update(ps,i<<1,l,r,bias);
    if(root[ps][i<<1|1].l<=r)
        Update(ps,i<<1|1,l,r,bias);
    PushUp(ps,i);
}

int Query(int ps,int i,int l,int r){
    if(l<=root[ps][i].l&&root[ps][i].r<=r)
        return root[ps][i].v;

    PushDown(ps,i);
    if(r<=root[ps][i<<1].r)
        return Query(ps,i<<1,l,r);
    else if(root[ps][i<<1|1].l<=l)
        return Query(ps,i<<1|1,l,r);
    else
        return max(Query(ps,i<<1,l,r),Query(ps,i<<1|1,l,r));
}

int main(){
    int cas=1,a,x,y;
    while(scanf("%d %d %d",&n,&m,&q)==3){
        memset(vis,false,sizeof vis);
        root.clear();
        cnt=0;
        for(int i=1;i<=n;i++){
            e[i].clear();
            scanf("%d",&r[i]);
            r[i]=Log(r[i]);
        }
        for(int i=1;i<=m;i++){
            scanf("%d %d %d",&a,&x,&y);
            a--;
            e[x].push_back(make_pair(y,a));
            e[y].push_back(make_pair(x,a));
        }
        for(int i=1;i<=n;i++)
            sort(e[i].begin(),e[i].end(),cmp);
        for(int i=1;i<=n;i++)
            if(!vis[i]){
                vis[i]=true;
                st=1;
                dfs(-1,i,0);
                root.push_back(vector<Node>(sz[i]<<2));
                Build(cnt++,1,1,sz[i]);
            }
        for(int i=1;i<=n;i++)
            Ist(bel[i],1,id[i],bs[i]);
        printf("Case #%d:\n",cas++);
        while(q--){
            scanf("%d %d %d",&a,&x,&y);
            t=Log(y);
            if(a==1){
                if(f[x]==x||(id[f[x]]<id[x]&&id[x]<id[f[x]]+rsz[f[x]]))
                    Update(bel[x],1,id[x]+rsz[x],id[x]+sz[x]-1,t-r[x]);//更新与该节点共线速度的子节点
                else
                    Update(bel[x],1,id[x],id[x]+rsz[x]-1,r[x]-t);//更新与该节点共角速度的子节点
                r[x]=t;
            }else
                printf("%.3f\n",log(2.0)*(t-Query(bel[x],1,id[x],id[x])+Query(bel[x],1,1,root[bel[x]][1].r)));
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值