【hdu 5692 Snacks】【dfs序】【線段樹】【好題】
阿新 • • 發佈:2018-11-08
【連結】
http://acm.hdu.edu.cn/showproblem.php?pid=5692
【題意】
樹上兩種操作:
0 x y 將x的權重變為y
1 x 求出從0出發,經過x的路徑的最距離和
【分析】
其實就是求一棵子樹x裡到0點路徑權值和最大的點的那個權值和,因為是對一棵子樹的所有值,所以容易想到用DFS序來處理。
dfs序處理出2*n的陣列,每個節點出現兩次,以x為根的子樹被包含在x出現的兩次的區間中,第二次出現表示不選當前這個點。
每個節點第一次出現的位置記錄一個它的價值v[i],在第二次出現的位置記錄一個-v[i]。那麼子樹中到根距離最多的點就是x那個區
間中字首和最大的點。用線段樹維護sum,mx(左字首)
【程式碼】
#pragma comment(linker, "/STACK:1024000000,1024000000") #include<bits/stdc++.h> using namespace std; #define ll long long const int maxn = 2e5 + 6; vector<int>v[maxn]; int in[maxn<<1], out[maxn<<1]; ll a[maxn<<1]; int cnt = 0; void dfs(int x,int f) { in[x] = ++cnt; for (int y : v[x])if(f!=y) { dfs(y, x); } out[x] = ++cnt; } struct node { int l, r; ll sum, mx; }t[maxn << 2]; void build(int p, int l, int r) { t[p].l = l; t[p].r=r; if (l == r) { t[p].sum = t[p].mx = a[l]; return; } int mid = l + r >> 1; build(p << 1, l, mid); build(p << 1 | 1, mid + 1, r); t[p].sum = t[p << 1].sum + t[p << 1 | 1].sum; t[p].mx = max(t[p << 1].mx, t[p << 1].sum + t[p << 1 | 1].mx); } void change(int p, int x, ll val) { if (t[p].l == t[p].r) { t[p].mx=t[p].sum = val; return; } int mid = t[p].l + t[p].r >> 1; if (x <= mid)change(p << 1, x, val); else change(p << 1 | 1, x, val); t[p].sum = t[p << 1].sum + t[p << 1 | 1].sum; t[p].mx = max(t[p << 1].mx, t[p << 1].sum + t[p << 1 | 1].mx); } ll query(int p, int x, int y) { if (t[p].l==x&&t[p].r==y) return t[p].mx; int mid = t[p].l + t[p].r >> 1; if (y <= mid)return query(p << 1, x, y); else if (x > mid)return t[p<<1].sum+query(p << 1 | 1, x, y); else return max(query(p << 1, x, mid), t[p << 1].sum + query(p << 1 | 1, mid + 1, y)); } int main() { int t; scanf("%d", &t); int ca = 0; while (t--) { printf("Case #%d:\n", ++ca); int n, m; scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++)v[i].clear(); for (int i = 1; i < n; i++) { int x, y; scanf("%d%d", &x, &y); x++; y++; v[x].push_back(y); v[y].push_back(x); } cnt = 0; dfs(1,1); for (int i = 1; i <= n; i++) { ll y; scanf("%lld", &y); a[in[i]] = y; a[out[i]] = -y; } build(1, 1, 2 * n); while (m--) { int op, x; ll y; scanf("%d%d", &op, &x); x++; if (op == 0) { scanf("%lld", &y); change(1, in[x], y); change(1, out[x], -y); } else { printf("%lld\n", query(1,in[x],out[x]-1));//注意out[x]表示已經走出x以及x的子樹了 } } } }