1. 程式人生 > >【CF160D】Edges in MST-最小生成樹+並查集

【CF160D】Edges in MST-最小生成樹+並查集

測試地址:Edges in MST 題目大意: 給定一個帶權無向連通圖,因為最小生成樹不唯一,於是問每一條邊是一定在最小生成樹上,還是可能在最小生成樹上,還是不可能在最小生成樹上。 做法: 本題需要用到最小生成樹+並查集。 這題挺神的。首先肯定需要構造出一棵最小生成樹,那麼樹上的邊的答案只有“一定”和“可能”兩種,而不在樹上的邊的答案只有“可能”和“不可能”兩種。 接下來先討論非樹邊的答案。根據最小生成樹的一些優美性質,這條邊的邊權一定大於等於樹中它兩個端點之間的路徑上邊權最大值,不然求出的樹就不是最小生成樹了。於是可知,當且僅當這條邊的邊權和樹上對應路徑邊權最大值相等的時候,這條邊才可能出現在最小生成樹中。怎麼判斷呢?將樹邊和非樹邊分別按邊權排序,用兩個指標,當非樹邊的邊權處理到x

x時,樹邊的邊權應該處理到x1x-1,處理的過程是,維護邊權小於xx的樹邊連成的連通塊。這樣一來,如果當前邊的兩個端點在這種狀態下沒有連通,就說明它們在樹上的路徑上有邊權大於x1x-1的邊,而這個邊權又必須x\le x,所以存在邊權等於xx的邊。反之,因為樹上兩點間有且僅有一條簡單路徑,顯然就不成立了。用並查集維護即可。 然後我們討論樹邊的答案。我們發現,在上述處理過程中,每當一條非樹邊被判為“可能”,那麼樹中它兩個端點之間的路徑上和它相等的邊就都是“可能”了。否則,因為沒有可能被任意一條非樹邊替換,所以剩下的邊就是“一定”在最小生成樹中了。但直接暴力複雜度很大,樹上差分又不能處理“標記一條路徑上所有長度為x
x
的邊”這樣的奇技淫巧,這怎麼做呢? 這時候這題最神的地方就出現了。因為這個xx這次被標記後,之後再標記xx,再標記這條路徑中這些邊就沒有必要了;而因為xx是這條路徑上邊權的最大值,而且這個xx是隨著演算法的進行而不斷增大的,所以之後標記比xx大的一些數跟這條路徑也沒什麼關係了。所以,我們在標記的同時,可以直接將這條路徑縮為一個點,因為後面的操作對這條路徑上的邊再沒有什麼影響了。這樣一來,因為每條邊只會被縮一次,配合並查集的使用,時間複雜度就是O(n)O(n)的了。 於是我們就解決了這一題,時間複雜度為O(nlogn)O(n\log n)以下是本人程式碼:

#include <bits/stdc++.h>
using namespace std;
int n,m,first[100010]={0},tot=0,pre[100010]={0},ans[100010]={0};
int dep[100010]={0},fa[100010],preid[100010],prew[100010],fak[100010];
struct kruskaledge
{
	int id,a,b,w;
	bool type;
}e[100010];
struct edge
{
	int id,v,w,next;
}ed[200010];

bool cmp(kruskaledge a,kruskaledge b)
{
	return a.w<b.w;
}

bool cmp2(kruskaledge a,kruskaledge b)
{
	if (a.w==b.w) return a.type<b.type;
	else return a.w<b.w;
}

void insert(int a,int b,int id,int w)
{
	ed[++tot].v=b;
	ed[tot].next=first[a];
	ed[tot].id=id;
	ed[tot].w=w;
	first[a]=tot;
}

int find(int *fa,int x)
{
	int r=x,i=x,j;
	while(fa[r]!=r) r=fa[r];
	while(i!=r) j=fa[i],fa[i]=r,i=j;
	return r;
}

void merge(int *fa,int x,int y)
{
	int fx=find(fa,x),fy=find(fa,y);
	fa[fx]=fy;
}

void kruskal()
{
	sort(e+1,e+m+1,cmp);
	for(int i=1;i<=n;i++)
		fa[i]=i;
	for(int i=1;i<=m;i++)
	{
		if (find(fa,e[i].a)!=find(fa,e[i].b))
		{
			e[i].type=1;
			merge(fa,e[i].a,e[i].b);
			insert(e[i].a,e[i].b,e[i].id,e[i].w);
			insert(e[i].b,e[i].a,e[i].id,e[i].w);
		}
		else e[i].type=0;
	}
}

void dfs(int v)
{
	for(int i=first[v];i;i=ed[i].next)
		if(ed[i].v!=pre[v])
		{
			pre[ed[i].v]=v;
			dep[ed[i].v]=dep[v]+1;
			preid[ed[i].v]=ed[i].id;
			prew[ed[i].v]=ed[i].w;
			dfs(ed[i].v);
		}
}

void up(int &x,int w)
{
	if (prew[x]==w) ans[preid[x]]=1;
	int nxt=find(fak,pre[x]);
	merge(fak,x,nxt);
	x=nxt;
}

void solve()
{
	sort(e+1,e+m+1,cmp2);
	for(int i=1;i<=n;i++)
		fa[i]=fak[i]=i;
	for(int i=1;i<=m;i++)
	{
		if (e[i].type) merge(fa,e[i].a,e[i].b);
		else
		{
			if (find(fa,e[i].a)!=find(fa,e[i].b))
			{
				ans[e[i].id]=1;
				int x=e[i].a,y=e[i].b;
				while(x!=y)
				{
					if (dep[x]==dep[y])
					{
						up(x,e[i].w);
						up(y,e[i].w);
					}
					else
					{
						if (dep[x]<dep[y]) swap(x,y);
						up(x,e[i].w);
					}
				}
			}
			else ans[e[i].id]=2;
		}
	}
}

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		e[i].id=i;
		scanf("%d%d%d",&e[i].a,&e[i].b,&e[i].w);
	}
	
	kruskal();
	dep[1]=1;
	dfs(1);
	solve();
	for(int i=1;i<=m;i++)
	{
		if (ans[i]==0) printf("any\n");
		if (ans[i]==1) printf("at least one\n");
		if (ans[i]==2) printf("none\n");
	}
	
	return 0;
}