您的位置:首页 > 运维架构

AOJ 2456 Usoperanto (树形dp)解题报告

2015-10-02 16:45 295 查看
Usoperanto

Time Limit: 8000ms

Memory Limit: 256000KB

This problem will be judged on Aizu. Original ID: 2456

64-bit integer IO format: %lld Java class name: Main

Prev

Submit Status Statistics Discuss

Next

Type:

None

None Graph Theory 2-SAT Articulation/Bridge/Biconnected Component Cycles/Topological Sorting/Strongly Connected Component Shortest Path Bellman Ford Dijkstra/Floyd Warshall Euler Trail/Circuit Heavy-Light Decomposition Minimum Spanning Tree Stable Marriage Problem Trees Directed Minimum Spanning Tree Flow/Matching Graph Matching Bipartite Matching Hopcroft–Karp Bipartite Matching Weighted Bipartite Matching/Hungarian Algorithm Flow Max Flow/Min Cut Min Cost Max Flow DFS-like Backtracking with Pruning/Branch and Bound Basic Recursion IDA* Search Parsing/Grammar Breadth First Search/Depth First Search Advanced Search Techniques Binary Search/Bisection Ternary Search Geometry Basic Geometry Computational Geometry Convex Hull Pick’s Theorem Game Theory Green Hackenbush/Colon Principle/Fusion Principle Nim Sprague-Grundy Number Matrix Gaussian Elimination Matrix Exponentiation Data Structures Basic Data Structures Binary Indexed Tree Binary Search Tree Hashing Orthogonal Range Search Range Minimum Query/Lowest Common Ancestor Segment Tree/Interval Tree Trie Tree Sorting Disjoint Set String Aho Corasick Knuth-Morris-Pratt Suffix Array/Suffix Tree Math Basic Math Big Integer Arithmetic Number Theory Chinese Remainder Theorem Extended Euclid Inclusion/Exclusion Modular Arithmetic Combinatorics Group Theory/Burnside’s lemma Counting Probability/Expected Value Others Tricky Hardest Unusual Brute Force Implementation Constructive Algorithms Two Pointer Bitmask Beginner Discrete Logarithm/Shank’s Baby-step Giant-step Algorithm Greedy Divide and Conquer Dynamic Programming Tag it!

Usoperanto

Usoperanto is an artificial spoken language designed and regulated by Usoperanto Academy. The academy is now in study to establish Strict Usoperanto, a variation of the language intended for formal documents.

In Usoperanto, each word can modify at most one other word, and modifiers are always put before modifiees. For example, with a noun uso (“truth”) modified by an adjective makka (“total”), people say makka uso, not uso makka. On the other hand, there have been no rules about the order among multiple words modifying the same word, so in case uso is modified by one more adjective beta (“obvious”), people could say both makka beta uso and beta makka uso.

In Strict Usoperanto, the word order will be restricted according to modification costs. Words in a phrase must be arranged so that the total modification cost is minimized. Each pair of a modifier and a modifiee is assigned a cost equal to the number of letters between the two words; the total modification cost is the sum of the costs over all modifier-modifiee pairs in the phrase. For example, the pair of makka and uso in a phrase makka beta uso has the cost of 4 for beta (four letters). As the pair of beta and uso has no words in between and thus the cost of zero, makka beta uso has the total modification cost of 4. Similarly beta makka uso has the total modification cost of 5. Applying the “minimum total modification cost” rule, makka beta uso is preferred to beta makka uso in Strict Usoperanto.

Your mission in this problem is to write a program that, given a set of words in a phrase, finds the correct word order in Strict Usoperanto and reports the total modification cost.

Input

The format of the input is as follows.

N

M0 L0



MN-1 LN-1

The first line contains an integer N (1 ≤ N ≤ 106). N is the number of words in a phrase.

Each of the following N lines contains two integers Mi (1 ≤ Mi ≤ 10) and Li (-1 ≤ Li ≤ N - 1, Li ≠ i) describing the i-th word (0 ≤ i ≤ N-1). Mi is the number of the letters in the word. Li specifies the modification: Li = -1 indicates it does not modify any word; otherwise it modifies the Li-th word.

Note the first sample input below can be interpreted as the uso-beta-makka case.

Output

Print the total modification cost.

Sample Input 1

3

3 -1

4 0

5 0

Output for the Sample Input 1

4

Sample Input 2

3

10 -1

10 0

10 1

Output for the Sample Input 2

0

Sample Input 3

4

1 -1

1 0

1 1

1 0

Output for the Sample Input 3

1

题目大意是:

给你n个单词,每个单词可以看做一个点。

其中某些单词可以修饰其他的单词,那么这个单词和被此单词修饰的单词的距离就是,他们之间夹住的所有单词的权值和,问,以某种顺序排列,使得这种权值和最小

如样例

给你4个点,第0个点的权值为1,他没有修饰的单词,第1个点权值为1,它修饰第0个单词,第2个点权1,修饰1,第三个点权1,修饰0

解析:

一开始想到贪心,然后排个序,没一会儿就找到反例了,显然,如果单词嵌套修饰的话,贪心是怎么都不行的。

然后我们可以利用修饰关系建立一种关系图,最上面的根节点就是不修饰任何点,最下面的叶子节点不被任何节点修饰,然后整个点集就变成了森林,对于每棵树,我们用树形dp做一次,求出根节点的最小花费,然后把这棵树打包,看成一个节点。

粗略的证明了下,这种求解方式是正确的。

5分钟想到解法,5分钟敲完,但是,但是,没想到100w个点的极限情况(一条线)直接把栈给爆了(赛场上re到哭)从来没写过手写栈,然后请教了栈手写的方法,终于撸过此题。

代码如下:

//
//  Created by Matrix on 2015-10-02
//  Copyright (c) 2015 Running Photon. All rights reserved.
//
//
//#pragma comment(linker, "/STACK:102400000,102400000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <sstream>
#include <set>
#include <vector>
#include <stack>
#define ALL(x) x.begin(), x.end()
#define INS(x) inserter(x, x,begin())
#define ll long long
#define CLR(x) memset(x, 0, sizeof x)
#define MAXN 9999
#define MAXSIZE 10
#define DLEN 4
using namespace std;
const int inf = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int maxn = 1e6 + 10;
const int maxv = 1e3 + 10;
const double eps = 1e-9;

inline int read() {
char c = getchar();
int f = 1;
while(!isdigit(c)) {
if(c == '-') f = -1;
c = getchar();
}
int x = 0;
while(isdigit(c)) {
x = x * 10 + c - '0';
c = getchar();
}
return x * f;
}
int n;
ll val[maxn];
vector <int> boot, G[maxn];
ll sum[maxn], dp[maxn];
int cur[maxn];
int stk[maxn];
ll a[maxn];
////void dfs(int u) {
////    dp[u] = 0;
////    sum[u] = val[u];
////    vector <ll> res;
////    for(int i = 0; i < G[u].size(); i++) {
////        int v = G[u][i];
////        dfs(v);
////        res.push_back(sum[v]);
////        sum[u] += sum[v];
////        dp[u] += dp[v];
////    }
////    sort(ALL(res), greater <ll> () ;
////    for(ll i = 1; i < res.size(); i++)
////        dp[u] += i * res[i];
//////  printf("dp[%d] = %lld\n", u, dp[u]);
////}
void calc(int u) {
int sz = 0;
sum[u] = val[u];
dp[u] = 0;
for(int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
a[sz++] = sum[v];
sum[u] += sum[v];
dp[u] += dp[v];
}
sort(a, a + sz, greater <ll> () );
for(ll i = 0; i < sz; i++) {
dp[u] += i * a[i];
}
}
void fun(int s) {
int top = 0;
stk[++top] = s;
while(top) {
int u = stk[top];
if(cur[u] < G[u].size()) {
int &i = cur[u];
int v = G[u][i];
stk[++top] = v;
i++;
}
else {
calc(u);
--top;
}
}
}
int main() {
#ifdef LOCAL
freopen("in.txt", "r", stdin);
//  freopen("out.txt","w",stdout);
#endif
while(scanf("%d", &n) != EOF) {
boot.clear(); CLR(cur);
ll ans = 0;
for(int i = 0; i < n; i++)
G[i].clear();
for(int i = 0; i < n; i++) {
int c, id;
scanf("%lld%d", &val[i], &id);
if(id == -1) {
boot.push_back(i);
}
else {
G[id].push_back(i);
}
}
for(int i = 0; i < boot.size(); i++) {
fun(boot[i]);
//          printf("dp[%d] = %lld\n", boot[i], dp[boot[i]]);
ans += dp[boot[i]];
}
printf("%lld\n", ans);
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  dp