Aizu Usoperanto 拓扑+贪心

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 saymakka 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

MN1  LN1

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

Each of the following N lines contains two integers Mi (1Mi10) and Li (−1LiN1Lii) describing the i-th word (0iN1). 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


题目链接:原题:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2456

BNU链接:http://www.bnuoj.com/v3/problem_show.php?pid=39572

题目大意和题解均引自:http://talk.icpc-camp.org/d/127-jag-summer-2012-day-4-j-usoperanto

【题意】
给你n([1,1e6])个单词,编号从0到n-1,
每个单词有两个属性(l,u),
l代表单词的长度,
u==-1代表这个单词不是其他任何单词的前缀,否则代表这个单词是u号单词的前缀,
然后每个前缀单词和它修饰单词之间字符数,记做这个前缀单词的成本。
我们希望安排这n个单词的顺序,使得总成本尽可能低

【类型】
贪心+拓扑

【分析】
我们从没有修饰能力的单词开始处理问题
以这个单词为根,由修饰词->被修饰词的关系,这题形成一个树结构。
我们考虑先放下被修饰词,然后在其前面顺序插入修饰词。

在过程中肯定满足:
插入其实一定只在头位置插入。
即,如果现在要放置一个单词,一定会先连续把修饰这个单词的子树全部放置完毕。
即,放置的两棵子树一定不会重叠。

证明:
首先要明确,每个点的成本,只会存在它和它的父节点之间。
我们可以想象现在只是放置了根节点(就是不修饰任何单词的单词)
然后开始放置它的所有子节点(就是修饰它的单词)。

根节点的子节点有x,y,x有子孙xson,y有子孙yson
我们发现先在当前决策,先放置x单词更优
决策最优被定义为,这个单词节点为根的子树的单词的长度和在当前最小
即|x|+|xson|<=|y|+|yson|

那么有决策
(1)x->y->xson->yson
(2)x->y->yson->xson
(3)x->xson->y->yson
成本分别是
(1)|x|+|y|+|xson|
(2)|x|+|y|+|yson|
(3)|x|+|xson|
显然先放完x的子树最优,类比归纳写一写也发现是这样。

平白一点,怎么证明呢?

放y先于放x的话,x会承受|y|的成本,y不会承受|x|的成本。
现在相当于,y(以及其他所有未放节点)也已经承受了|x|的成本,x造成的成本已经最劣、最大化了。
然后xson替代了x,也还会继续承受|y|(和接下来其他子树上)的成本。
这种决策,所承受的成本,与其他不放x,先放别的单词的情况,没有什么差别。
同时额外,xson和x的距离被拉大了。xson也开始承受更多成本。
一看就不是最优。肯定不能这么放。
于是。有:如果放一个节点的话,也会一并放置完这个节点为根的子树。

有了这个原则。现在的问题就变成了。
先x子树还是先y子树好呢?
我们发现成本成了∑ li , i为x子树和∑ lj , j为y子树的大小比较。贪小的即可。
于是可以从叶节点开始,利用类似于拓扑的方式bfs,然后就可以AC啦。


AC代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <vector>
#include <limits.h>
#include <queue>
#include <stack>
#include <map>
#include <set>
using namespace std;
typedef long long LL;
#define pll pair<LL, LL>
#define pii pair<int, int>
#define X first
#define Y second
#define MAXN 1000010//1e3
#define lson l, mid, (rt << 1)
#define rson mid + 1, r, (rt << 1 | 1)
const double eps = 1e-10;
int n;
vector<int> root, edge[MAXN];
LL value[MAXN], cost[MAXN];
bool cmp(int i, int j) {
    return value[i] < value[j];
}
void init() {
    root.clear();
    for(int i = 0; i <= n; i++) {
        edge[i].clear();
        cost[i] = -1;
    }
}
void solve(int rt) {
    queue<int> q;
    stack<int> s;
    q.push(rt); s.push(rt);
    while(!q.empty()) {
        int tmp = q.front();
        q.pop();
        int len = edge[tmp].size();
        for(int i = 0; i < len; i++) {
            q.push(edge[tmp][i]);
            s.push(edge[tmp][i]);
        }
    }
    while(!s.empty()) {
        int tmp = s.top();
        s.pop();
        sort(edge[tmp].begin(), edge[tmp].end(), cmp);
        int len = edge[tmp].size();
        cost[tmp] = 0LL;
        LL res = 0;
        for(int i = 0; i < len; i++) {
            cost[tmp] += res + cost[edge[tmp][i]];
            res += value[edge[tmp][i]];
        }
        value[tmp] += res;
    }
}
int main() {
    while(~scanf("%d", &n)) {
        init();
        int x;
        for(int i = 0; i < n; i++) {
            scanf("%lld%d", &value[i], &x);
            if(x == -1) root.push_back(i);
            else edge[x].push_back(i);
        }
        int len = root.size();
        LL ans = 0;
        for(int i = 0; i < len; i++) {
            solve(root[i]);
            ans += cost[root[i]];
        }
        printf("%lld\n", ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值