51nod 1789 跑的比谁都快
#include <stdio.h> #include <algorithm> #include <iostream> #include <string.h> #include <stack> #define MAXN 100050 #define INF 2000000000000000000LL #define LL long long using namespace std; int n, p, numEdge, top, root; bool lef[MAXN]; int head[MAXN], star[MAXN]; LL dp[MAXN], ans; struct Edge { int v, next; }edge[MAXN]; void addEdge(int u, int v) { edge[numEdge].v = v; edge[numEdge].next = head[u]; head[u] = numEdge++; } struct Node { int id, l, r; Node(int I = 0, int L = 0, int R = 0): id(I), l(L), r(R){}; }S[MAXN]; LL quickPow(LL a, LL b) { LL res = 1LL; while(b) { if(b & 1) res *= a; a *= a; b >>= 1; } return res; } LL work(int i, int j) { return quickPow(j - i, p) + (LL)star[i]; } void dfs(int u) { stack<Node> tmp; int tmpR = -1; int lft = 1, rit = top, mid; if(u == root) { dp[u] = 0; S[++top] = Node(root, 1, n); } else { while(lft <= rit) //初始化dp[u] { mid = (lft + rit) >> 1; if(S[mid].l <= u && u <= S[mid].r) { dp[u] = dp[S[mid].id] + work(S[mid].id, u); break; } if(u < S[mid].l) rit = mid - 1; else lft = mid + 1; } while(top > 0) //决策单调性的经典操作 { if(dp[S[top].id] + work(S[top].id, S[top].l) < dp[u] + work(u, S[top].l)) { lft = S[top].l, rit = S[top].r; while(lft <= rit) //二分找出breakpoint { mid = (lft + rit) >> 1; if(dp[S[top].id] + work(S[top].id, mid) < dp[u] + work(u, mid)) lft = mid + 1; else rit = mid - 1; } tmpR = S[top].r; S[top].r = rit; break; } tmp.push(S[top]); //记录下从栈中撤出的元素到tmp中 top--; } if(lft <= n) S[++top] = Node(u, lft, n); } if(head[u] == -1) ans = min(ans, dp[u]); for(int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].v; dfs(v); } if(u != root) { if(lft <= n) top--; if(tmpR != -1) S[top].r = tmpR; while(!tmp.empty()) //回溯的时候恢复栈 { S[++top] = tmp.top(); tmp.pop(); } } } int main() { scanf("%d%d", &n, &p); star[0] = numEdge = top = 0; for(int i = 0; i <= n; i++) head[i] = -1, lef[i] = true; root = 1; for(int fa, i = 1; i <= n; i++) { scanf("%d%d", &star[i], &fa); addEdge(fa, i); lef[fa] = false; } ans = INF; dfs(root); cout << ans << endl; return 0; }
题目链接: http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1789
一个很明显的树上dp 不过对于 维护每条链 就行了
问题在于如何优化 这是个很伤脑筋的问题,看了一个其他人的代码,还是不理解那边二分的作用是啥!QAQ
还是菜啊~
好了,终于明白了那大概是个什么玩意儿了! 这是一种决策性单调dp,采用的是单调栈的优化
相当于是广义的单调栈吧(口胡)
二分的作用是为了找出那个栈所含括的点,也就是栈里那个元素表示最优的最大区间(当然在程序没有结束前都只是暂时最优,也就是说右区间是可以修改的)
设那个元素为elem 那么如果elem.l >= elem.r 那么显然这个元素可以被踢出栈去了
可以二分的原因在于因为这是单调的(其实得事先证明的)
为什么我认为它是广义的单调栈呢?
因为我们普通的栈优化其实每次每个栈元素对应的区间是 elem.r = n的,所以不需要记录。
详情请见:
1D/1D动态规划优化初步
http://wenku.baidu.com/link?url=wJZ5nc-evTz4OjhPRD8yA6zZUeANcrngwYRMOAAySeCXBFvyAcrFHusd1cG3Yz8A5Ez-whuHY6nV55tx9yYLVzHJKPegne2iOZqSzqWHYbu