NOI2014 魔法森林

2022-08-16 00:51:20 字數 1302 閱讀 3835

前三題都是模板,是為了讓我們練手的。

這題才是真正的挑戰。

首先,一看這題既不加邊也不刪邊,甚至連樹都不是,還是道靜態題,並且長得還賊像最小生成樹,我就納悶了,這是lct?

還真是。

我們可以將邊按照\(a\)排序。之後,我們維護乙個關於\(b\)權值的動態最小生成樹。

具體做法是,建立乙個lct,這個lct維護的是最大值的下標。之後,按照\(a\)遞增的順序遍歷所有的邊。如果這條邊連線了兩塊尚未被連線的區域,那不要廢話直接連;否則,這肯定構成乙個環。我們在lct中\(split\)出來這個環在當前mst上的部分,並找到這裡面的最大值的位置。如果最大值比當前這條邊的\(b\)還要大,那麼斷掉最大值的邊並用當前邊代替肯定更優。因此我們就這麼做。在每加完一條邊後,如果當前節點\(1\)和節點\(n\)是連通的,那麼\(split(1,n)\),並將(當前的\(a\)+[\(1-n\)鏈上的最大值])與當前答案取\(\min\)。

哦,另外,為了將邊權轉成點權,我們將每條邊視作乙個點

核心**:

for(int i=1;i<=n+m;i++)t[i].val=0,t[i].mx=dsu[i]=i;

for(int i=n+1;i<=n+m;i++)t[i].val=e[i-n].b;

for(int i=1;i<=m;i++)

bool merge(int x,int y)

struct edge

}e[100100];

struct lctt[500100];

void pushup(int x)

void rev(int x)

void pushdown(int x)

int identify(int x)

void rotate(int x)

void pushall(int x)

void splay(int x)

}void access(int x)

void makeroot(int x)

int findroot(int x)

int split(int x,int y)

void link(int x,int y)

void cut(int x,int y)

int main()

if(find(1)==find(n))res=min(res,e[i].a+t[split(1,n)].val);

} if(res==0x3f3f3f3f)puts("-1");

else printf("%d\n",res);

return 0;

}

NOI2014 魔法森林

為了得到書法大家的真傳,小e同學下定決心去拜訪住在魔法森林中的隱士。魔法森林可以被看成乙個包含個n節點m條邊的無向圖,節點標號為 1 n 邊標號為1 m 初始時小e同學在 1 號節點,隱士則住在 n 號節點。小e需要通過這一片魔法森林,才能夠拜訪到隱士。魔法森林中居住了一些妖怪。每當有人經過一條邊的...

NOI2014 魔法森林

為了得到書法大家的真傳,小 e 同學下定決心去拜訪住在魔法森林中的隱士。魔法森林可以被看成乙個包含 n 個節點 m 條邊的無向圖,節點標號為1,2,3,n,邊標號為 1,2,3,m。初始時小 e 同學在 1 號節點,隱士則住在 n 號節點。小 e 需要通過這一片魔法森林,才能夠拜訪到隱士。魔法森林中...

NOI2014 魔法森林

noi2014 魔法森林 要求a 與 b 的總和最小 可以按a排序 再以b為權值維護一顆樹 lct維護最小生成樹 要解決的問題 將每一條邊變為乙個點 邊權變為點權 舉個栗子 u v有一條邊權為w的邊 怎lct連邊方式為 u new v new的點權為w 不斷維護最小生成樹 如果新加入的邊的 u與v不...