1. 程式人生 > 實用技巧 >題解:POI2012 Salaries

題解:POI2012 Salaries

題解:POI2012 Salaries

Description

The Byteotian Software Corporation (BSC) has \(n\) employees.

In BSC's strict hierarchy, each employee has a direct supervisor, except the CEO, to whom all other BSC employees answer, directly or not.

Each employee has a unique monthly salary, and all their salaries range from 1 to \(n\)

bythalers.

Each supervisor earns more than each of their subordinates.

According to Byteotian law, the salaries of employees on certain posts may be publicly disclosed.

Furthermore, if the salary of an employee is disclosed, then the salary of their supervisor is also disclosed.

The Byteotian Internal Revenue Anti-Corruption Service (BIRAS) has decided to investigate BSC.

Before BIRAS enters BSC with a warrant, they intend to learn the salaries of all BSC employees that are not disclosed but can be determined from those that are disclosed.

題意:

給一棵 \(n\) 個結點的樹,點權取 \(1 \sim n\) 且 各不相同。

滿足任意非根結點的權值一定比它的父節點權值小。

現在自頂向下地已知一些點的權值(即若某非根點權值已知,其父節點的權值也一定已知),

問哪些點的權值能唯一確定。

\(n \leq 1e6\)



Algorithm

這是一道 十分有趣 而 一言難盡 的題目。


我們先從兩個例子入手,試圖理解一下「唯一確定」的意思。

例一

在這裡,由於點 \(b\) 是點 \(3\) 的孫子結點,最大隻能取 \(1\) ,因此唯一確定 \(b = 1\)

同時,點 \(a\) 是點 \(3\) 的兒子結點,可能的取值有 \(1, 2\) ,但因為 \(b = 1\) ,所以唯一確定 \(a = 2\)


例二

此處 \(a, b\) 都是 \(3\) 的子節點,他們的取值可能為 \(1, 2\) ,但並不能唯一確定一種對應關係。

\(c\)\(5\) 的子節點,可能的取值有 \(1, 2, 4\) 。但因為 \(a, b\) 對應 \(1, 2\) ,只能有 \(c = 4\) ,這也是唯一確定的。


揆諸上例與題面自頂向下給權的性質,我們容易發現:

  1. 對於任意一個未確定的結點,它的可能取值範圍總是 \((0,x_i]\)

    其中, \(x_i\) 是由樹的結構與已知點權共同決定的。

  2. 一個點的權能被唯一確定,當且僅當其取值範圍為 \((x_i - 1, x_i]\)

  3. 當我們確定了 \(k\) 個結點的取值後,剩餘的未確定結點的取值範圍就會變化為 \((k, x_i]\)

  4. 確定點權的順序總是從小到大的。

(寫成左開右閉的形式僅僅是為了規避區間左右端點相等的寫法,沒別的意思)


接下來只要模擬上述的推理即可。

我們可以首先一次 DFS 遍歷整棵樹,根據樹結構維護每個點可能的最大取值。

特別注意此處有個坑,如果僅根據點的祖先結點取值和深度計算 \(x_i\) 的話程式碼就是錯誤的。
經過這種方法計算出的值 \(x'_i\) 可能已經被某個已知點取了,你需要嘗試不斷減少 \(x'_i\) 的值直到其符合定義為止。
這個"不斷減少"的過程可以預處理出來。

DFS 之後,我們可以根據最大取值將點排序,再依次考慮每個權是否可以被確定或唯一確定。

這個演算法描述起來還挺模糊的……不如直接讀程式碼:

#include<bits/stdc++.h>
using namespace std;

template<class T>
inline void read(T &x)
{
	char c = getchar();	x = 0;
	while(c < '0' || '9' < c)	c = getchar();
	while('0' <= c && c <= '9')
	{
		x = (x << 1) + (x << 3) + c - 48;
		c = getchar();
	}
}

const int INF = 0x7f7f7f7f;
typedef pair<int, int> Node;
vector<Node> ord;

template<const int N, const int M>
class Tree {
private:
	int beg[N], nex[M], tar[M], len;
public:
	int vap[N], van[N];
	bool vis[N];

	Tree():len(1) {}
	inline void add_egde(int a, int b)
	{
		++len, tar[len] = b;
		nex[len] = beg[a], beg[a] = len;
	}

	void dfs(int cur, int val)
	{
		if(!vap[cur])	ord.push_back(Node(val, cur));
		for(int i = beg[cur]; i; i = nex[i])
		{
			if(vap[tar[i]])	dfs(tar[i], vap[tar[i]]);
			else			dfs(tar[i], van[val - 1]);
		}
	}
};

Tree<1048576, 1048576> T;
int main()
{
	int n, rot;

	read(n);
	for(int i = 1, x; i <= n; ++i)
	{
		read(x), read(T.vap[i]);
		
		if(x == i)	rot = i, T.vap[i] = n;
		else		T.add_egde(x, i);

		if(T.vap[i])
			T.vis[T.vap[i]] = true;
	}

	for(int i = 1; i <= n; ++i)
	{
		if(T.vis[i])	T.van[i] = T.van[i - 1];
		else			T.van[i] = i;		
	}

	T.dfs(rot, n);
	sort(ord.begin(), ord.end());

	int done = 0, len = ord.size();
	for(int i = 1, j = 0; i <= n; i++)
	{
		if(T.vis[i])	done++;
		else
		{
			int cnt = 0;
			while(j < len && ord[j].first == i)	j++, cnt++;
			if(cnt == 1 && done == i - 1)
				T.vap[ord[j - 1].second] = i;
			done += cnt; 
		}
	}

	for(int i = 1; i <= n; ++i)
		printf("%d\n", T.vap[i]);

	return 0;
}