冰精冻西瓜[P3787洛谷]
题目描述
琪露诺是拥有操纵冷气程度的能力的妖精,一天她发现了一片西瓜地。这里有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
说明
子任务可能出现如下的特殊性质:
“西瓜树”退化为一条链
输入数据中的实数均保留8位小数,选手的答案被判作正确当且仅当输出与标准答案误差不超过10^-7。请特别注意浮点数精度问题。
实际数据中,冷气的寒冷程度x的范围为 [-0.1,0.1]
题解:
很巧妙的树状数组,线段树也可以
首先运用了“砍树”思想,因为当wi=0时,该边无法传递冻气。将整棵树以wi=0的
边为界,分成多棵树,每一棵树都求dfs并编号,记录一个点影响的区间(子树)
in[i]~out[i](就是子树dfs序号的范围,当然包括自己),
并记下每个点到该树的根的w之积
对于操作①,可以这么想,如果所有冷气都是从树根释放出来的,
那么冷冻值可以直接累加起来,最后乘以ki就能得到任意子节点的冷冻值
因此我们可以把每个操作①看做从树根释放的,那么冷冻值就是x/ki。
所以只要给冷气释放的节点和其子树都增加x/ki即可。
由于有线段树的支持,所以单次操作时间复杂度是O(logn)
对于操作⑨,只要输出树状数组(线段树)上对应节点的值乘以ki即可,时间复杂度也是O(logn)
如果不砍树,则该点的冻气的就会往wi=0的下面蔓延,不合题意。
(此处wi指边权,ki指树根到i的wi之积,与代码变量有出入)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<queue> 6 #include<cmath> 7 #define ld double 8 using namespace std; 9 int n,m; 10 vector<int>G[100005]; 11 vector<ld>dis[100005]; 12 const ld eps=1e-8; 13 queue<int>q; 14 bool vis[100005]; 15 int in[100005],cnt,out[100005]; 16 ld c[100005],w[100005]; 17 void add(int u,int v,ld d) 18 { 19 G[u].push_back(v);dis[u].push_back(d); 20 G[v].push_back(u);dis[v].push_back(d); 21 } 22 void dfs(int x) 23 {int i; 24 in[x]=++cnt; 25 vis[x]=1; 26 int l=G[x].size(); 27 for (i=0;i<l;i++) 28 { 29 if (vis[G[x][i]]==0&&dis[x][i]>0) 30 { 31 w[G[x][i]]=1.00000000*w[x]*dis[x][i]; 32 dfs(G[x][i]); 33 } 34 else if (vis[G[x][i]]==0) 35 q.push(G[x][i]); 36 } 37 out[x]=cnt; 38 } 39 void update(int x,ld d) 40 {int i; 41 for (i=x;i<=n;i+=(i&(-i))) 42 c[i]+=d; 43 } 44 ld query(int x) 45 {int i; 46 ld s=0; 47 for (i=x;i;i-=(i&(-i))) 48 s+=c[i]; 49 return s; 50 } 51 int main() 52 {int i,u,v,k,x; 53 ld d,y; 54 cin>>n; 55 for (i=1;i<=n-1;i++) 56 { 57 scanf("%d%d%lf",&u,&v,&d); 58 add(u,v,d); 59 } 60 q.push(1); 61 cnt=0; 62 while (!q.empty()) 63 { 64 w[q.front()]=1; 65 dfs(q.front()); 66 q.pop(); 67 } 68 scanf("%d",&k); 69 for (i=1;i<=k;i++) 70 { 71 int ch; 72 scanf("%d",&ch); 73 if (ch==1) 74 { 75 scanf("%d%lf",&x,&y); 76 ld p=y/w[x]; 77 update(in[x],p); 78 update(out[x]+1,-p); 79 } 80 else 81 { 82 scanf("%d",&x); 83 printf("%.8lf\n",query(in[x])*w[x]); 84 } 85 } 86 }