這題面不禁讓我聯想到了樹剖
思路顯然,這道題並不需要樹剖畢竟它只是藍題,不過,它也需要用到\(dfs\)序,把樹上操作轉化成序列操作(這個思想可以說很套路了),但這題有乙個麻煩的地方:在修改子樹時每一條邊都會對修改值造成影響,而這用線段樹是難以維護的。
於是我們考慮將邊上的影響分離出來,可以先預處理出乙個從當前節點到根的字首積,在每次子樹加時將加數除以當前節點的字首積,查詢時再乘上查詢節點的字首積,我們發現,通過這樣的操作,就將修改節點到根節點的字首積乘回來了。是不是很妙
but還有乙個細節:資料範圍中特意強調了\(w_i\)可能為\(0\),而我們知道,當\(w_i\)為0時,修改其他節點(指除i及其子樹以外的節點)就對i節點及其子樹沒有影響了。為了方便地處理,我們可以巧妙地將這些\(w_i\)為\(0\)的點也設為根,將原樹拆分成幾棵樹遍歷,這樣就可以解決\(0\)的問題且子樹編號仍然連續了。同時,我們每乙個點仍然只會遍歷一次(為根節點兩次),所以遍歷仍然是\(o(n)\)級別的。
**
#include #include #include using namespace std;
const int maxn = 2e5 + 10; //兩倍空間開了嗎
int n,head[maxn],num,m;
int rm[maxn],rt[maxn],top;
double tim[maxn];
struct edgee[maxn];
void add(int u, int v, double val); head[u] = num;}
int dfn[maxn],id[maxn],cnt,siz[maxn];
void dfs(int u, int f)
}}struct seg_tree
void downdate(int l, int r, int p)
}void add(int l, int r, int l, int r, int p, double val)
downdate(l, r, p);
int mid = (l + r) >> 1;
if(l <= mid) add(l, r, l, mid, lc(p), val);
if(mid < r) add(l, r, mid + 1, r, rc(p), val);
c[p] = c[lc(p)] + c[rc(p)];
}double query(int pos, int l, int r, int p)
}tree;
void push(int u, double val)
double get_ans(int u)
int main() tim[1] = 1, dfs(1, 0);
for(int i = 1; i <= top; ++ i) dfs(rt[i], 0);
scanf("%d", &m);
while(m--)
else printf("%.8lf\n", get_ans(id));
}return 0;
}
luogu P3787 冰精凍西瓜
嘟嘟嘟 好題,好題 看這個修改和詢問,就知道要麼是求完dfs序後線段樹維護,要麼是樹剖。又因為這道題都是子樹的操作,沒有鏈上的,所以線段樹就夠了。然而重點不是這個。這道題最麻煩的是線段樹pushdown時對於每乙個節點打的標記都不一樣,因為每一條邊上的能力值不一樣。這也是這道題最巧妙的一點 我們把每...
bzoj3787 Gty的文藝妹子序列
我們回憶bzoj3289的做法,可以使用莫隊演算法,加上線段樹進行茲瓷in,out,query。我們回憶經典分塊做法。預處理ans i,j 表示第i塊到第j塊的答案,sum i,j 表示前i塊元素j的個數,然後只需要再弄個樹狀陣列就可以搞了。現在我們要茲瓷修改,那我們照著原來的思路改一下。ans i...
BZOJ3787 Gty的文藝妹子序列
將序列分成 sqrt 塊,預處理出每兩塊之間的逆序對數,以及ap i 表示前i塊內數字出現次數的樹狀陣列 預處理 o n sqrt log n 修改時,ap i 可以在 o sqrt log n 複雜度內完成修改,然後考慮修改的位置對答案的貢獻,可以發現相當於某一行 某一列都加上乙個數,對於行列各開...