luogu P3787 冰精冻西瓜
好题,好题……
看这个修改和询问,就知道要么是求完dfs序后线段树维护,要么是树剖。又因为这道题都是子树的操作,没有链上的,所以线段树就够了。
然而重点不是这个。这道题最麻烦的是线段树pushdown时对于每一个节点打的标记都不一样,因为每一条边上的能力值不一样。这也是这道题最巧妙的一点:我们把每一次对节点 i 放的冷气都转移到从根节点放的,这样pushdown的标记就统一了。
具体操作是啥咧:假如u到跟要经过w1, w2, w3这三条边,那么我们对u放x的冷气,就相当于从根节点放Div[u] = w1 * w2 * w3 * x的冷气,查询v的冷气值的时候把线段树得到的答案除以Div[v]即可。预处理Div[i]dfs一遍即可。
然而这道题还没完,因为Div[i]可以等于0,所以这棵树实际上会断成一个森林。所以就有好多个根节点,以及好多棵线段树,但是空间大小是不变的。
所以要记录每一棵树的根节点,再从根节点开始dfs,维护的Div[i]实际上表示的是 i 到他所在的树的根节点的Πwi。(一定是从根节点开始!我就是因为从树中任意一棵节点开始导致85分,今天听bin哥讲完后突然才想出来这个问题)
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<algorithm> 5 #include<cstring> 6 #include<cstdlib> 7 #include<cctype> 8 #include<vector> 9 #include<stack> 10 #include<queue> 11 using namespace std; 12 #define enter puts("") 13 #define space putchar(' ') 14 #define Mem(a, x) memset(a, x, sizeof(a)) 15 #define rg register 16 typedef long long ll; 17 typedef double db; 18 const int INF = 0x3f3f3f3f; 19 const db eps = 1e-9; 20 const int maxn = 1e5 + 5; 21 inline ll read() 22 { 23 ll ans = 0; 24 char ch = getchar(), last = ' '; 25 while(!isdigit(ch)) {last = ch; ch = getchar();} 26 while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();} 27 if(last == '-') ans = -ans; 28 return ans; 29 } 30 inline void write(ll x) 31 { 32 if(x < 0) x = -x, putchar('-'); 33 if(x >= 10) write(x / 10); 34 putchar(x % 10 + '0'); 35 } 36 37 int n, m; 38 struct Edge 39 { 40 int nxt, to; 41 db w; 42 }e[maxn << 1]; 43 int head[maxn], ecnt = -1; 44 void addEdge(int x, int y, db w) 45 { 46 e[++ecnt] = (Edge){head[x], y, w}; 47 head[x] = ecnt; 48 } 49 50 int st[maxn], top = 0; 51 bool vis[maxn]; 52 int rt[maxn], bel[maxn]; 53 int siz[maxn], dfsx[maxn], pos[maxn], cnt = 0; 54 db Div[maxn]; 55 bool zero(db x) 56 { 57 return x >= -eps && x <= eps; 58 } 59 void dfs(int now, int id) 60 { 61 vis[now] = 1; siz[now] = 1; bel[now] = id; 62 dfsx[now] = ++cnt; pos[cnt] = now; 63 for(int i = head[now]; i != -1; i = e[i].nxt) 64 { 65 if(vis[e[i].to]) continue; 66 if(zero(e[i].w)) st[++top] = e[i].to; 67 else 68 { 69 Div[e[i].to] = Div[now] * e[i].w; 70 dfs(e[i].to, id); 71 siz[now] += siz[e[i].to]; 72 } 73 } 74 } 75 76 int ls[maxn << 2], rs[maxn << 2], ctr = 0; 77 db sum[maxn << 2], lzy[maxn << 2]; 78 void pushdown(int now) 79 { 80 if(!zero(lzy[now])) 81 { 82 if(!ls[now]) ls[now] = ++ctr; 83 if(!rs[now]) rs[now] = ++ctr; 84 sum[ls[now]] += lzy[now]; sum[rs[now]] += lzy[now]; 85 lzy[ls[now]] += lzy[now]; lzy[rs[now]] += lzy[now]; 86 lzy[now] = 0; 87 } 88 } 89 void update(int l, int r, int L, int R, int& now, db d) 90 { 91 if(!now) now = ++ctr; 92 if(l == L && r == R) {sum[now] += d; lzy[now] += d; return;} 93 pushdown(now); 94 int mid = (l + r) >> 1; 95 if(R <= mid) update(l, mid, L, R, ls[now], d); 96 else if(L > mid) update(mid + 1, r, L, R, rs[now], d); 97 else update(l, mid, L, mid, ls[now], d), update(mid + 1, r, mid + 1, R, rs[now], d); 98 } 99 db query(int l, int r, int& now, int id) 100 { 101 if(!now) now = ++ctr; 102 if(l == r) return sum[now] * Div[pos[id]]; 103 pushdown(now); 104 int mid = (l + r) >> 1; 105 if(id <= mid) return query(l, mid, ls[now], id); 106 else return query(mid + 1, r, rs[now], id); 107 } 108 109 int main() 110 { 111 Mem(head, -1); 112 n = read(); 113 for(int i = 1; i < n; ++i) 114 { 115 int x = read(), y = read(); 116 db w; scanf("%lf", &w); 117 addEdge(x, y, w); addEdge(y, x, w); 118 } 119 Div[1] = 1.00; dfs(1, 1); 120 for(int i = 1; i <= top; ++i) Div[st[i]] = 1.00, dfs(st[i], st[i]); 121 m = read(); 122 for(int i = 1; i <= m; ++i) 123 { 124 int d = read(); 125 if(d == 1) 126 { 127 int x = read(); 128 db w; scanf("%lf", &w); 129 update(dfsx[bel[x]], dfsx[bel[x]] + siz[bel[x]] - 1, dfsx[x], dfsx[x] + siz[x] - 1, rt[bel[x]], w / Div[x]); 130 } 131 else 132 { 133 int x = read(); 134 printf("%.8lf\n", query(dfsx[bel[x]], dfsx[bel[x]] + siz[bel[x]] - 1, rt[bel[x]], dfsx[x])); 135 } 136 } 137 return 0; 138 }