【LCT】城市旅行(luogu 4842/金牌导航 LCT-3)
正題
luogu 4842
金牌導航 LCT-3
題目大意
給你一棵樹,讓你進行一些操作:
1.刪除一條邊
2.連接一條邊
3.給一條路徑上的點加上x
4.給出一條路徑,在該路徑選取兩個點,求這兩個點之間路徑的權值和的期望值
解題思路
該樹可以用LCT維護
因為答案用分數表示,且計算過程中所有路徑的權值和不會大于longlong,所以維護路徑權值和然后除以總方案數即可
對于每個節點,維護以下信息
1.s:子樹內的路徑權值和
2.sz:子樹大小
3.sum:子樹的點權和
4.num:作為左/右子樹時的貢獻(每個節點被計算的次數不同)
對于計算一顆子樹的s
左子樹會被計算szrs+1sz_{rs}+1szrs?+1遍,右子樹會被計算szls+1sz_{ls}+1szls?+1遍,還有當前節點會被計算szls×szrssz_{ls}\times sz_{rs}szls?×szrs?遍
那么有sx=sls+srs+(szls+1)×numrs,1+(szrs+1)×numls,0+vx×(szls+1)?(szrs+1)s_x = s_{ls} + s_{rs} + (sz_{ls} + 1) \times num_{rs,1} + (sz_{rs} + 1) \times num_{ls,0} + v_x \times (sz_{ls} + 1) * (sz_{rs} + 1)sx?=sls?+srs?+(szls?+1)×numrs,1?+(szrs?+1)×numls,0?+vx?×(szls?+1)?(szrs?+1)
對于計算num,當作為左子樹時,左子樹的貢獻不變,而每一次計算左子樹時右子樹和當前點也會被計算,所以要加上(szls+1)×(sumrs+vx)(sz_ls+1)\times (sum_rs + v_x)(szl?s+1)×(sumr?s+vx?),作為右子樹時同理
其他信息就很好維護了,對此建立LCT即可
代碼
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ll long long #define N 50010 using namespace std; ll n, m, x, y, z, nm[N]; struct LCT {#define ls son[x][0]#define rs son[x][1]ll v[N], s[N], p[N], pn[N], fa[N];ll sz[N], sum[N], num[N][2], son[N][2];bool NR(ll x){return fa[x] && (son[fa[x]][0] == x || son[fa[x]][1] == x);}bool IRS(ll x){return son[fa[x]][1] == x;}void push_up(ll x){s[x] = s[ls] + s[rs] + (sz[ls] + 1) * num[rs][1] + (sz[rs] + 1) * num[ls][0] + v[x] * (sz[ls] + 1) * (sz[rs] + 1);num[x][0] = num[ls][0] + (v[x] + sum[rs]) * (sz[ls] + 1) + num[rs][0];num[x][1] = num[rs][1] + (v[x] + sum[ls]) * (sz[rs] + 1) + num[ls][1];sum[x] = sum[ls] + sum[rs] + v[x];sz[x] = sz[ls] + sz[rs] + 1;return;}void pushr(ll x){swap(ls, rs);swap(num[x][0], num[x][1]);//翻轉時作為左/右子樹的貢獻會改變p[x] ^= 1;return;}void push_add(ll x, ll y){s[x] += nm[sz[x]] * y;//nm為所有方案下的點數和,在下文計算到num[x][0] += sz[x] * (sz[x] + 1) / 2 * y;//在維護時會計算到的次數num[x][1] += sz[x] * (sz[x] + 1) / 2 * y;sum[x] += sz[x] * y;v[x] += y;pn[x] += y;return; }void push_down(ll x){if (p[x]){if (ls) pushr(ls);if (rs) pushr(rs);p[x] = 0;}if (pn[x]){if (ls) push_add(ls, pn[x]);if (rs) push_add(rs, pn[x]);pn[x] = 0;}return;}void push_hall(ll x){if (NR(x)) push_hall(fa[x]);push_down(x);return;}void rotate(ll x){ll y = fa[x], z = fa[y], k = IRS(x), g = son[x][!k];if (NR(y)) son[z][IRS(y)] = x;if (g) fa[g] = y;son[x][!k] = y;son[y][k] = g;fa[y] = x;fa[x] = z;push_up(y);return;}void Splay(ll x){push_hall(x);while(NR(x)){if (NR(fa[x])){if (IRS(x) == IRS(fa[x])) rotate(fa[x]);else rotate(x);}rotate(x);}push_up(x);return;}void access(ll x){for (ll y = 0; x; x = fa[y = x])Splay(x), rs = y, push_up(x);return;}void make_root(ll x){access(x);Splay(x);pushr(x);return;}ll find_root(ll x){access(x);Splay(x);while(ls) push_down(x), x = ls;Splay(x);return x;}bool Split(ll x, ll y){make_root(x);if (find_root(y) != x) return 0; Splay(y);return 1;}void link(ll x, ll y){make_root(x);if (find_root(y) != x) fa[x] = y;return;}void cut(ll x, ll y){make_root(x);if (find_root(y) == x && fa[y] == x && !son[y][0]){fa[y] = rs = 0;push_up(x);}return;} }T; ll gcd(ll x, ll y) {if (!y) return x;return gcd(y, x % y); } void solve(ll x, ll y) {ll z = gcd(x, y);printf("%lld/%lld\n", x / z, y / z);return; } int main() {scanf("%lld%lld", &n, &m);nm[1] = 1;for (ll i = 2; i <= n; ++i)nm[i] = nm[i - 1] + i * (i + 1) / 2;for (ll i = 1; i <= n; ++i)scanf("%lld", &T.v[i]);for (ll i = 1; i < n; ++i){scanf("%lld%lld", &x, &y);T.link(x, y);}while(m--){scanf("%lld%lld%lld", &z, &x, &y);if (z == 1) T.cut(x, y);else if (z == 2) T.link(x, y);else if (z == 3){scanf("%lld", &z);if (T.Split(x, y)) T.push_add(y, z);}else{if (!T.Split(x, y)) puts("-1");else solve(T.s[y], T.sz[y] * (T.sz[y] + 1) / 2);}}return 0; }總結
以上是生活随笔為你收集整理的【LCT】城市旅行(luogu 4842/金牌导航 LCT-3)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 江珊个人资料 江珊代表作有哪些
- 下一篇: 【KMP】重复子串(ybtoj KMP-