17AHU排位赛3 D题 旋转吧!雪月花 ! (DFS序,线段树维护树上最值)

problem

在一个平面上有n个齿轮,每个齿轮都有自己的初始半径 ri 。有n-1对齿轮是互相嵌在一起的,即它们拥有相同的线速度。如果将n个齿轮当作n个点,将n-1条相嵌关系当作n-1条边,那么这些齿轮会组成一个树。即树上每条边连接的两个点代表的齿轮拥有相同的线速度。
现在有两种操作:
——操作1:将第x个齿轮的半径改成y
——操作2:如果给第x个齿轮一个角速度y,那么所有齿轮都旋转了起来,问所有齿轮中,角速度最大的是多少。
本题中,所有齿轮的半径以及角速度都是2的正整数次幂。
在此题中,你不需要考虑和某个齿轮嵌套的其它齿轮会发生冲突挤压。

Input

第一行两个整数n,m(1<=n<=200000,1<=m<=200000),分别表示齿轮的个数以及操作的个数。
第二行n个整数 ri ,表示每个齿轮的初始半径。(1<= ri <= 230 ,且 ri ​ = 2k )
接下来n-1行,每行两个整数x,y,表示第x个齿轮与第y个齿轮具有相同的线速度。
接下来m行描述m个操作(op,x,y)
若op=1则表示操作1,将第x个齿轮的半径改成y(1<=y<= 230 ,且y= 2k )
若op=2则表示操作2,给第x个齿轮一个角速度y(1<=y<= 230 ,且y= 2k )

Output

对于每个操作2,输出最大的角速度。
数据保证最大的角速度一定是整数,但有可能会比较大,所以输出答案对 109 +7取模

Input

3 3
4 2 8
1 2
1 3
2 1 2
1 2 4
2 2 1

Output

4
1

Limitation

1s 256MB

Hint

对于操作1,第1个齿轮的角速度是2,第2个齿轮角速度是4,第3个齿轮角速度是1
对于操作3,第1个齿轮的角速度是1,第2个齿轮角速度是1,第3个齿轮角速度是 12

传送门传送门传送门传送门


思路

角速度最大的齿轮即为半径最小的齿轮,因为每次旋转它们的线速度相同

比赛时我是用dfs序映射到线段树,然后线段树维护的。

不dfs序也行,因为和边、子树好像没有什么关系,只涉及到半径的修改和最小半径的查找


代码示例

#include<bits/stdc++.h>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
typedef long long ll;
const int mod=1e9+7;

const int maxn=200100;
const long long inf=3e18;

struct Edge{
    int from,to;
}edges[maxn<<1];

int head[maxn<<1],in[maxn],out[maxn],Hash[maxn];
int Clock,cnt;
ll rr[maxn<<2],val[maxn];//rr线段树维护最小值
//ll lazy[maxn<<2];  单点修改
//val是齿轮的半径

void edge_add(int u,int v){
    edges[cnt].from=v;
    edges[cnt].to=head[u];
    head[u]=cnt++;
}

void init(){
    memset(head,-1,sizeof(head));
    cnt=0;
    Clock=0;
}

void dfs(int rt,int f){
    in[rt]=++Clock;
    Hash[Clock]=rt;//这个编号的齿轮就对应着这个rt结点
    //有了这个就能利用线段树找价值了
    for(int i=head[rt];i!=-1;i=edges[i].to){
        int tt=edges[i].from;
        if(tt!=f) dfs(tt,rt);
    }
    out[rt]=Clock;
}

void PushUp(int rt){
    rr[rt]=min(rr[rt<<1],rr[rt<<1|1]);
}

void build(int l,int r,int rt){
    rr[rt]=inf;//赋大值
    if(l==r){
        rr[rt]=val[Hash[l]];
        return ;
    }
    int m=(l+r)>>1;
    build(lson); build(rson);
    PushUp(rt);
}

void update(int p,ll cov,int l,int r,int rt){
    //p点需要修改为cov这个新的半径
    if(l==r){
        rr[rt]=cov;//这里是赋值
        return ;
    }
    int m=(l+r)>>1;
    if(p<=m) update(p,cov,lson);
    else update(p,cov,rson);
    PushUp(rt);
}

ll query(int L,int R,int l,int r,int rt){
    if(L<=l&&r<=R) return rr[rt];
    int m=(l+r)>>1;
    ll ret=inf;//很大数
    if(L<=m) ret=min(ret,query(L,R,lson));//维护最值
    if(R>m) ret=max(ret,query(L,R,rson));
    return ret;
}

int main()
{
    ios::sync_with_stdio(false);
    //freopen("read.txt","r",stdin);
    int n,m;//n个点,m个操作
    cin>>n>>m;
    for(int i=1;i<=n;++i){
        cin>>val[i];
    }
    init();
    int u,v,op;
    for(int i=0;i<n-1;++i){
        cin>>u>>v;
        edge_add(u,v);
        edge_add(v,u);
    }
    dfs(1,0);
    build(1,Clock,1);
    for(int i=0;i<m;++i){
        cin>>op;
        if(op==1){
            int p;
            ll cov;
            cin>>p>>cov;
            update(in[p],cov,1,Clock,1);
            val[p]=cov;
        }
        else{
            int p;
            ll cov;//角速度
            cin>>p>>cov;
            ll xian=val[p]*cov;
            ll minr=query(1,Clock,1,Clock,1);
            //cout<<xian<<" 线速度"<<endl;
            //cout<<minr<<" 最小半径"<<endl;
            cout<<(xian/minr)%mod<<endl;
        }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值