冰精冻西瓜

冰精冻西瓜

琪露诺是拥有操纵冷气程度的能力的妖精,一天她发现了一片西瓜地。这里有n个西瓜,由n-1条西瓜蔓连接,形成一个有根树,琪露诺想要把它们冷冻起来慢慢吃。这些西瓜蔓具有神奇的性质,可以将经过它的冷气的寒冷程度放大或缩小,每条西瓜蔓放大/缩小冷气寒冷程度的能力值为Wi,表示冷气经过它后,寒冷程度值x会变为x*wi。每个西瓜也有一个寒冷程度值,炎热的夏日,所有西瓜的寒冷程度值初始都为0。琪露诺会做出两种动作:①.对着西瓜i放出寒冷程度为x的冷气。这股冷气顺着西瓜蔓向“西瓜树”的叶子节点蔓延,冷气的寒冷程度会按照上面的规则变化。遇到一个西瓜连了多条西瓜蔓时,每条叶子节点方向的西瓜蔓均会获得与原先寒冷程度相等的冷气。途径的所有西瓜的寒冷程度值都会加上冷气的寒冷程度值。⑨.向你询问西瓜i的寒冷程度值是多少。等等,为什么会有⑨?因为笨蛋琪露诺自己也会忘记放了多少冰呢。所以,帮她计算的任务就这么交给你啦。
输入输出格式
输入格式:
第一行一个整数n,表示西瓜的数量。
西瓜编号为1~n,1为这棵“西瓜树”的根。
接下来n-1行,每行有两个整数u,v和一个实数w,表示西瓜u和西瓜v之间连接有一条藤蔓,它放大/缩小冷气寒冷程度的能力值为w。
接下来一行一个整数m,表示操作的数量。
接下来m行,每行两个或三个整数。
第一个数只能是1或9。
如果为1,接下来一个整数i和一个实数x,表示对西瓜i放出寒冷程度为x的冷气。
如果为9,接下来一个整数i,表示询问编号为i的西瓜的寒冷程度值。
输出格式:
对于每个操作⑨,输出一行一个实数,表示对应西瓜的寒冷程度值。
样例输入:
4
1 2 1.00000000
2 3 0.00000000
3 4 1.00000101
9
1 1 3.00000000
9 2
9 3
1 2 1.42856031
9 4
9 2
1 3 4.23333333
9 2
9 4
样例输出:
3.00000000
0.00000000
0.00000000
4.42856031
4.42856031
4.23333761

洛谷p3787

一开始以为是暴力模拟吧……最近老是暴力暴力(xaero教导我们要暴力拿到分数于是)暴力不出来,……还是太菜了,后来rym大佬提醒我是dfs序加上线段树或者树状数组,(???为什么好好的树要区间处理然后他强调了dfs序(我真菜,真的)然后告诉我了线段树做法)
—————————————废话分割线———————————————
1.对树进行一个dfs序的处理,为了处理线段树。
来画个图
这里写图片描述
原谅我这个**图画的有点粗糙
1.譬如从1,2分别开始传播冷气开始传播冷气,那么先把这些冷气值全部加到3,最后处理的时候分别乘上该叶子结点3到1,2的距离(即西瓜蔓的处理冷气值)
2.但是,这里是不是有一步你觉得难以处理,就是怎么分别乘上叶子结点呢?相信到此时你已经明白了怎么用线段树来处理传播冷气,那么,这道题第一关键的就是
3.把每次输入进来的冷气用相同比例使之相等于从根输入进来的冷气,譬如从2输入冷气,就按照2到1的西瓜蔓的值反推上去,然后统一按照每个节点到根的距离来计算。
4.还有一个关键是砍树。就是存在那种西瓜蔓冷气值为0 点,那么就砍树,把这个节点作为新树的新根。

其他具体看代码,应该都写出来了把?有问题的话就加我qq问哦!

#include<bits/stdc++.h>
using namespace std;
struct edge{
    int t;
    double lt;
};
edge E[2000100];
vector<int> g[1000100];
int cnt,n,m,tail[1000100],pre[1000100],vi[1000100],num[1000100],root[1000100],cc,tt;
double tree[4000100],tag[4000100],val[1000100];

void dfs(int u,int fa,double vl){
    vi[u]=1;
    val[u]=vl;//从根来的时候变化的k
    num[++cnt]=u;//在链中的节点在书中的编号
    pre[u]=cnt;//该节点在链中的新编号
    for(int i=0;i<g[u].size();i++){
        int v=E[g[u][i]].t;
        double ll=E[g[u][i]].lt;
        if(fa==v||vi[v]) continue;
        if(ll==0){//如果该节点未被问并且边是0则断开
            root[++cc]=v;//把新的根存下来
            continue;
        }
        dfs(v,u,vl*ll);
    }
    tail[u]=cnt;//该点所管辖的区间的结尾
}

void build(int l,int r,int k){//线段树建树
    if(l==r){
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,k<<1);
    build(mid+1,r,k<<1|1);
    return;
}

void pd(int k){
    if(tag[k]==0.0) return;
    tag[k<<1]+=tag[k];
    tag[k<<1|1]+=tag[k];
    tree[k<<1]+=tag[k];
    tree[k<<1|1]+=tag[k];
    tag[k]=0;
}

void add(int l,int r,int a,int b,double vl,int k){//区间更新
    pd(k);
    if(l>=a&&r<=b){
        tree[k]+=vl;
        tag[k]+=vl;
        return;
    }
    int mid=(l+r)>>1;
    if(mid>=a) add(l,mid,a,b,vl,k<<1);
    if(mid<b) add(mid+1,r,a,b,vl,k<<1|1);
}

double search(int l,int r,int k,int tar){//单点查询
    pd(k);
    if(l==r&&l==tar){
        return tree[k]*val[num[tar]];//记得乘上val
    }
    int mid=(l+r)>>1;
    if(mid<tar) return search(mid+1,r,k<<1|1,tar);
    else return search(l,mid,k<<1,tar);
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        int x,y;
        double l;
        scanf("%d%d%lf",&x,&y,&l);
        if(l==0) tt++;
        E[++cnt]=(edge){y,l};//一开始存双向边,因为不知道怎么连
        g[x].push_back(cnt);
        E[++cnt]=(edge){x,l};
        g[y].push_back(cnt);
    }
    cnt=0;
    dfs(1,1,1);
    for(int i=1;i<=cc;i++){
        dfs(root[i],root[i],1);
    }
    build(1,cnt,1);
    scanf("%d",&m);
    for(int i=1;i<=m;i++){
        int k,x;
        double y;
        scanf("%d",&k);
        if(k==9){
            scanf("%d",&x);
            printf("%.8f\n",search(1,cnt,1,pre[x]));
        }
        else{
            scanf("%d%lf",&x,&y);
            y/=val[x];//化为相对值
            add(1,cnt,pre[x],tail[x],y,1);
        }
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值