読者です 読者をやめる 読者になる 読者になる

Lilliput Steps

小さな一歩から着実に. 数学やプログラミングのことを書きます.

APIO 2012-1 - Dispatching

問題文 : 忍者の派遣

解法 :
各頂点でpriority queueを持ち, dfs を行い, 給料のコストが大きい頂点から予算を超える分だけqueueから取り除く.

これだけではO(N * NlogN) = O(N^2 log N) の計算量となってしまうが, 頂点を併合する際に, 短い方のqueueを併合することでO(N * log^2 N) = O(N log^2 N) 時間で同等のdfs が行える.

下線部に該当する処理は1 行くらいしか書いていないのだが, それでO(N) -> O(log N)に落ちるので激アツである.

(参考)


JOI 2012 春合宿 Day1-Building2 の解説pdf

コード :

#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>

#define MAX_N (100000)

using namespace std;

typedef long long lint;

lint res;

int N, M;

priority_queue<int> pool[MAX_N];
vector<int> G[MAX_N];
int cost[MAX_N], mult[MAX_N];
lint sum[MAX_N];

priority_queue<int> *dfs(int v)
{
	priority_queue<int> *pq = &pool[v];
	pq->push(cost[v]);
	sum[v] += cost[v];
	
	for (int i = 0; i < G[v].size(); i++){
		priority_queue<int> *pq2 = dfs(G[v][i]);
		if (pq->size() < pq2->size()) swap(pq, pq2);
		
		while (pq2->size()){
			pq->push(pq2->top()); pq2->pop();
		}
		
		sum[v] += sum[G[v][i]];
		while (sum[v] > M){
			sum[v] -= pq->top(); pq->pop();
		}
	}
	
	res = max(res, (lint)mult[v] * pq->size());
	
	return (pq);
}

int main()
{
	scanf("%d %d", &N, &M);
	
	for (int i = 0; i < N; i++){
		int par;
		scanf("%d %d %d", &par, cost + i, mult + i);
		if (~(--par)) G[par].push_back(i);
	}
	
	dfs(0);
	
	printf("%lld\n", res);
}