生成樹相關模板(續)

發現一個寫太多編輯器比較卡。繼續,這也就是一些小點了。
完全圖生成樹計數
Cayley公式:f(n)=n(n2)f(n) = n*(n-2)

有向圖樹形圖計數
樹形圖計數仍適用Matrix-Tree定理,其Kirchhoff矩陣: 外向樹:所計算的矩陣中的對角線元素代表的是點的出度,而且除對角線元素外,代表的是又向的路是否存在(存在就設爲-1);內向圖樹:對角線元素代表的是點的出度。

最小曼哈頓距離生成樹
給你一堆二維平面的n個點,兩兩點之間按照曼哈頓距離連邊,問最小生成樹。如果兩兩連邊,邊數量會炸。

/*
曼哈頓距離生成樹
用兩個座標軸和y=x, y=-x把二維區間分成8個部分. 緊挨y軸正方向右側的一個區域記爲R1
有結論: 如果枚舉每個點作爲原點 這八個部分每個部分只可能分別連接一個最近點. 
而在R1種對於 代原點(x0, y0), 有以下關係
xk >= x0 && yk-xk >= y0 - x0
我們根據x排序離線樹狀數組詢問yi-xi. 問(xi+yi)最小值及點的編號. 
連邊.

*/


// POJ 3241
// 找生成樹第k大的邊. 
const int N =2e5+5;
const int mod = 31011;
const int inf = 0x3f3f3f3f;
int c[N], id[N]; // c[] 樹狀數組最小值, id[] 樹狀數組最小值位置. 
// 樹狀數組也可以維護最值, 但是隻能ask前綴, 沒法看區間. 
void init()
{ // 初始化數組數組.
	memset(c, inf, sizeof(c));
	memset(id, -1, sizeof(id));
}
void add(int x, int val, int _id)
{ // 在x點插入值, 這個點(xi, yi) : xi+yi = val, i= _id;
	for (; x; x -= x & -x) if (c[x] > val)
	{
		c[x] = val; id[x] = _id;
	}
}
int ask(int x, int m)
{ // 詢問大於x = (yi - xi) 的 (xi+yi) 最小值
	int minval = inf;
	int ans = -1;
	for (; x <= m; x += x & -x) if (minval > c[x])
	{
		minval = c[x]; ans = id[x];
	}
	return ans;
}
struct Node
{ //存點
	int x, y; 
	int id;
	friend bool operator < (const Node & a, const Node & b)
	{
		if (a.x !=b.x) return a.x < b.x;
		return a.y < b.y;
	}
} pos[N];
struct Ed
{ //存邊
	int x, y, w;
	Ed() {}
	Ed(int x, int y, int w)
	: x(x), y(y), w(w) {}
	friend bool operator < (const Ed & a, const Ed & b)
	{
		return a.w < b.w;
	}
}ed[N], resed[N];
int tot, restot, cntt;
int fa[N], a[N], b[N];
inline int getn(int x)
{ //離散找值
	return lower_bound(b+1, b+cntt, x) - b;
}
inline int getdis(int i, int j)
{
	return abs(pos[i].x - pos[j].x) + abs(pos[i].y - pos[j].y);
}
void build(int n)
{ //建樹
    // 按照x座標排序
	sort(pos+1, pos+n+1);
	for (int i = 1; i <= n; i++)
		a[i] = b[i] = pos[i].y - pos[i].x; // 準備離散
	sort(b+1, b+n+1);
	cntt = unique(b+1, b+n+1) - b;
	init();
	for (int i = n; i >= 1; i--)
	{ // 離線操作, 邊查邊加
		int poss = getn(a[i]);
		int ans = ask(poss, cntt-1);
		if (ans != -1) // 有邊建邊
			ed[++tot] = Ed(pos[i].id, pos[ans].id, getdis(i, ans));
        
		add(poss, pos[i].x+pos[i].y, i);
        // 查完了, 把這個點壓進數組
	}
}
void tran(int n)
{ // 座標轉化, 將R1~R4 依次轉到R1位置. 每個建一遍邊. 
	for (int dir = 1; dir <= 4; dir++)
	{
		if (dir == 2 || dir == 4)
		{
			for (int i = 1; i <= n; i++)
				swap(pos[i].x, pos[i].y);
		}
		else if (dir == 3)
		{
			for (int i = 1; i <= n; i++)
				pos[i].x = -pos[i].x;
		}
		build(n);
	}
}
int fi(int x)
{
	if (x == fa[x]) return x;
	return fa[x] = fi(fa[x]);
}
void kruskal(int n)
{  // Kruskal過程
	restot = 0;
	for (int i = 1; i <= n; i++) fa[i] = i;
	sort(ed+1, ed+tot+1);
	for (int i = 1; i <= tot; i++)
	{
		int x = ed[i].x, y = ed[i].y;
		int _x = fi(x), _y = fi(y);
		if (_x != _y)
		{
			fa[_x] = _y;
			resed[++restot] = Ed(x, y, ed[i].w);
		}
	}
}
int main()
{
	int n, k;
	scanf("%d%d", &n, &k);
	tot = 0; restot = 0;
	for (int i = 1; i <= n; i++)
	{
		scanf("%d%d", &pos[i].x, &pos[i].y);
		pos[i].id = i;
	}
	tran(n);
	kruskal(n);
	sort(resed+1,resed+restot+1);
	printf("%d\n", resed[restot - k + 1].w);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章