NOI2014 魔法森林

2022-05-10 23:57:03 字數 4451 閱讀 5980

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

魔法森林中居住了一些妖怪。每當有人經過一條邊的時候,這條邊上的妖怪 就會對其發起攻擊。幸運的是,在 1 號節點住著兩種守護精靈:a 型守護精靈與 b 型守護精靈。小 e 可以借助它們的力量,達到自己的目的。

只要小 e 帶上足夠多的守護精靈,妖怪們就不會發起攻擊了。具體來說,無 向圖中的每一條邊 ei 包含兩個權值 ai 與 bi 。若身上攜帶的 a 型守護精靈個數不 少於 ai ,且 b 型守護精靈個數不少於 bi ,這條邊上的妖怪就不會對通過這條邊 的人發起攻擊。當且僅當通過這片魔法森林的過程中沒有任意一條邊的妖怪向 小 e 發起攻擊,他才能成功找到隱士。

由於攜帶守護精靈是一件非常麻煩的事,小 e 想要知道,要能夠成功拜訪到 隱士,最少需要攜帶守護精靈的總個數。守護精靈的總個數為 a 型守護精靈的 個數與 b 型守護精靈的個數之和。

輸入格式:

輸入檔案的第 1 行包含兩個整數 n,m,表示無向圖共有 n 個節點,m 條邊。 接下來 m 行,第i+ 1 行包含 4 個正整數 xi,yi,ai,bi,描述第i條無向邊。 其中xi與 yi為該邊兩個端點的標號,ai 與 bi 的含義如題所述。 注意資料中可能包含重邊與自環。

輸出格式:

輸出一行乙個整數:如果小 e 可以成功拜訪到隱士,輸出小 e 最少需要攜 帶的守護精靈的總個數;如果無論如何小 e 都無法拜訪到隱士,輸出「-1」(不 含引號)。

輸入樣例#1:複製

4 5 

1 2 19 1

2 3 8 12

2 4 12 15

1 3 17 8

3 4 1 17

輸出樣例#1:複製
32
輸入樣例#2:複製
3 1 

1 2 1 1

輸出樣例#2:複製
-1
如果小 e 走路徑 1→2→4,需要攜帶 19+15=34 個守護精靈; 如果小 e 走路徑 1→3→4,需要攜帶 17+17=34 個守護精靈; 如果小 e 走路徑 1→2→3→4,需要攜帶 19+17=36 個守護精靈; 如果小 e 走路徑 1→3→2→4,需要攜帶 17+15=32 個守護精靈。 綜上所述,小 e 最少需要攜帶 32 個守護精靈。

每條邊有兩個權值,合在一起維護十分不便,考慮將它們分開。

將所有邊按權值a從小到大排序,每一次加入一條邊,找一下1~n的所有路徑中權值b的最大值的最小值,然後用amax+bmax更新答案。

為什麼這樣做是對的呢,如果bmax所在的路徑並不是amax所在的路徑,那麼我們在放入amax之前,bmax就已經和乙個比amax要小的權值更新了答案,所以更優解已經被計算在內了。

然後每次spfa不需要memset,直接將當前加入的邊的兩端點入隊就好。

這道題沒有設計卡spfa的資料,所以spfa可以水過去。

1

//never forget why you start

2 #include3 #include4 #include5 #include6 #include7 #include8 #include9

#define inf (2000000000)

10using

namespace

std;

11int n,m,ans=inf;

12 queuemem;

13struct

edgee[100005

];16

struct

nodeedge[200005

];19

int head[50005

],size;

20void putin(int

from,int to,int dis1,int

dis2)

28int dist[50005

];29

bool cmp(const edge a,const

edge b)

32int vis[50005

];33

void spfa(int x,int

y)48}49

}50}51

}52intmain()

67if(ans==707406379)printf("

-1\n");

68else printf("

%d\n

",ans);

69return0;

70 }

但是spfa的複雜度是無法保證的,如果考場上要穩過的話就需要乙個複雜度更加穩定的演算法。

這題的正解是lct,思維難度還是很高的,首先我們將邊化為點,如果一條邊連線x和y兩個點,我們可以認為是乙個點分別和x,y兩點相連。這樣我們就可以將權值資訊放到中間那個點中。

然後還是考慮排序,先按權值a排序,從小到大加邊,每次加一條邊,就相當於是link一下x,再link一下y。

每次判斷1和n是否聯通,如果聯通,就找路徑上的最大值更新答案就好。

如果我們在連線的時候發現x和y是聯通的,如果我們直接聯通就會產生環,那麼我們考慮將這個環上最大的邊刪掉,因為乙個環上最大的邊是沒有存在的意義的。(當我們要經過這條最大的邊的時候,我們可以從環的另外一邊繞過去)

注意:我們要刪掉的是這個環上最大的邊,所以如果新加入的邊的權值比原路徑的最大值還要大,我們就沒有必要加入了。(在這裡被坑了好久)

1

//never forget why you start

2 #include3 #include4 #include5 #include6 #include7 #include8

#define ll(x) lct[x].child[0]

9#define rr(x) lct[x].child[1]

10#define son(x,t) lct[x].child[t]

11#define inf (2147483647)

12using

namespace

std;

13int n,m,ans=inf;

14int

read()

17while(i>='

0'&&i<='9')

18return ans*f;19}

20struct

edge

25 }e[100005

];26

struct

lctlct[200005

];30

void push_up(int

x)37

void push_rev(int

x)42

void push_down(int

x)48}49

void push(int

x)53

int getson(int

x)56

void rotate(int

x)67

void splay(int

x)73

void access(int

x)while

(x);82}

83void mroot(int

x)88

void link(int u,int

v)92

void cut(int u,int

v)100

struct

fa107

int find(int

x)111

void merge(int x,int

y)117

}118

bool judge(int x,int

y)122

}fa;

123int ppos[200005

];124

intmain()

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

140 sort(e+1,e+m+1

);141

for(i=1;i<=m;i++)ppos[e[i].id]=i;

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

156}

157if(flag||!fa.judge(u,v))

163if(fa.judge(1

,n))

169}

170if(ans==inf)printf("

-1\n");

171else printf("

%d\n

",ans);

172return0;

173 }

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不...