日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【学习笔记】左偏树的可持久化(【模板】k短路 / [SDOI2010]魔法猪学院)

發布時間:2023/12/3 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【学习笔记】左偏树的可持久化(【模板】k短路 / [SDOI2010]魔法猪学院) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • description
  • solution
  • code

【模板】k短路 / [SDOI2010]魔法豬學院

description

iPig 在假期來到了傳說中的魔法豬學院,開始為期兩個月的魔法豬訓練。經過了一周理論知識和一周基本魔法的學習之后,iPig 對豬世界的世界本原有了很多的了解:眾所周知,世界是由元素構成的;元素與元素之間可以互相轉換;能量守恒…………\ldots…

iPig 今天就在進行一個麻煩的測驗。iPig 在之前的學習中已經知道了很多種元素,并學會了可以轉化這些元素的魔法,每種魔法需要消耗 iPig 一定的能量。作為 PKU 的頂尖學豬,讓 iPig 用最少的能量完成從一種元素轉換到另一種元素…等等,iPig 的魔法導豬可沒這么笨!這一次,他給 iPig 帶來了很多 1 號元素的樣本,要求 iPig 使用學習過的魔法將它們一個個轉化為 N 號元素,為了增加難度,要求每份樣本的轉換過程都不相同。這個看似困難的任務實際上對 iPig 并沒有挑戰性,因為,他有堅實的后盾…現在的你呀!

注意,兩個元素之間的轉化可能有多種魔法,轉化是單向的。轉化的過程中,可以轉化到一個元素(包括開始元素)多次,但是一但轉化到目標元素,則一份樣本的轉化過程結束。iPig 的總能量是有限的,所以最多能夠轉換的樣本數一定是一個有限數。具體請參看樣例。
輸入格式

第一行三個數 N,M,E,表示 iPig 知道的元素個數(元素從 1 到 N 編號),iPig 已經學會的魔法個數和 iPig 的總能量。

后跟 M 行每行三個數 si,ti,ei? 表示 iPig 知道一種魔法,消耗 ei? 的能量將元素 si 變換到元素 ti

solution

對于原圖以終點ttt為根建立一棵任意的最短路徑樹TTT,即我們所謂的反圖

ttt跑出到所有點的最短路disdisdis

顯然,從點sss到終點ttt可能有不止一條路徑,也有可能不止一條最短路

如果兩條邊都能出現在TTT上,且兩條邊又只能出現一條,那么隨便選一條即可

定義:對于一條原圖邊u→vu\rightarrow vuvuuu為這條邊的起點,vvv為這條邊的終點

顯然,如果一條原邊出現在TTT上,那么vvv一定是uuu的祖先

這個最短路徑樹有幾個性質

  • 性質Ⅰ

    對于一條s→ts\rightarrow tst的路徑,將這條路徑依次經過的邊的集合,記為邊集EEE,或路徑EEE

    然后去掉EEETTT的交集,記為邊集E′E'E,或路徑E′E'E

    也就是說,一條路徑PPP指代的是這條路徑包含的所有邊的集合

    那么對于E′E'E中任意相鄰的兩條邊(從原圖的方向s→ts\rightarrow tst來看)a,ba,ba,b,即先走邊aaa再走邊bbb

    滿足bbb的起點在TTT中為aaa的終點的祖先或者相同點

    因為a,ba,ba,b兩邊之間要么由樹邊相連,要么直接相連

  • 性質Ⅱ

    對于不在TTT的一條邊eeeu,vu,vu,v分別為起點/終點,邊權為www

    定義Δe=disv+w?disu\Delta_e=dis_v+w-dis_uΔe?=disv?+w?disu?,即選這條邊與最短路的差值

    LEL_ELE?為一條路徑的長度,這條路徑為s→ts\rightarrow tst

    顯然有LE=∑e∈E′Δe+dissL_E=\sum_{e\in E'}\Delta_e+dis_sLE?=eE?Δe?+diss?

  • 性質Ⅲ

    對于滿足性質Ⅰ的中E′E'E的定義的邊集PPP,有且只有一條s→ts\rightarrow tst的路徑EEE,使得P=E′P=E'P=E

    因為TTT上任意兩點之間有且僅有一條路徑


這道題就轉化為了求第KKK小滿足性質Ⅰ的路徑E′E'E的定義的邊集

最短路徑肯定是TTT上的dis1dis_1dis1?

用小根堆維護邊集EEE,初始為空集(事實上只要維護當前刻畫出的路徑的最后一條邊的起點(尾端)即可,空集就是整條路徑的起點sss,本題中就是點111

一個點可以延伸多條邊,也就刻畫出了多條不同的路徑,這些路徑都需要分開記錄

對于已刻畫的所有路徑,取出最小權值的路徑EEE,設當前尾部邊的起點為uuu

有兩種新路徑的刻畫

  • 將這個邊集的最后一條邊,替換成另外一條以uuu為起點的權值大于等于當前邊權值的非樹邊
  • 這條最后邊的終點為vvv,在這條邊的后面接一條 在TTT中為vvv祖先(含vvv自身)的點 能延伸的所有非樹邊的 最小邊

問題就是怎么維護祖先延伸的所有非樹邊的最小邊

從祖先轉移過來,最小堆合并即可

每個點的自身信息也要保留,不能和祖先的邊混合,這是為了轉移Ⅰ,替換最后一條邊

那么可持久化即可

?:最后注意一下,以nnn為起點的邊(這種魔法可以將nnn元素轉化成其他元素)是不能要的
因為本題,到了nnn元素就意味著這次轉化成功了,就此就終止了

你谷第一個數據點就是卡的這個小細節

code

#include <queue> #include <cstdio> #include <vector> #include <cstring> #include <iostream> using namespace std; #define Pair pair < double, int > #define inf 0x7f7f7f7f #define maxm 200005 #define maxn 5005 struct edge { int u, v; double w; }E[maxm]; struct node { int lson, rson, dis, End; double val; }t[maxm * 50]; priority_queue < Pair, vector < Pair >, greater < Pair > > q; vector < int > G[maxn]; int n, m, cnt; double En; int f[maxn], root[maxn]; double dis[maxn]; bool vis[maxn], used[maxm];int Newnode( double val, int End ) {int now = ++ cnt;t[now].End = End;t[now].val = val;return now; }int merge( int x, int y ) {if( ! x or ! y ) return x + y;if( t[x].val > t[y].val ) swap( x, y );int now = ++ cnt;t[now] = t[x];t[now].rson = merge( t[x].rson, y );if( t[t[now].lson].dis < t[t[now].rson].dis )swap( t[now].lson, t[now].rson );t[now].dis = t[t[now].rson].dis + 1;return now; }void dfs( int u ) {vis[u] = 1;for( auto id : G[u] ) {auto v = E[id].v;auto w = E[id].w;if( ! vis[v] and dis[v] == dis[u] + w )f[v] = u, used[id] = 1, dfs( v );} }int main() {scanf( "%d %d %lf", &n, &m, &En );for( int i = 1;i <= m;i ++ ) {scanf( "%d %d %lf", &E[i].v, &E[i].u, &E[i].w );if( E[i].v == n ) i --, m --;else G[E[i].u].push_back( i );}memset( dis, 0x7f, sizeof( dis ) );q.push( make_pair( dis[n] = 0, n ) );while( ! q.empty() ) {int u = q.top().second; q.pop();if( vis[u] ) continue;vis[u] = 1;for( auto id : G[u] ) {auto v = E[id].v;auto w = E[id].w;if( dis[v] > dis[u] + w )q.push( make_pair( dis[v] = dis[u] + w, v ) );}}memset( vis, 0, sizeof( vis ) );dfs( n );for( int i = 1;i <= m;i ++ )if( used[i] ) continue;else {auto u = E[i].u;auto v = E[i].v;auto w = E[i].w;if( dis[u] == inf or dis[v] == inf ) continue;else root[v] = merge( root[v], Newnode( dis[u] + w - dis[v], u ) );}for( int i = 1;i <= n;i ++ ) q.push( make_pair( dis[i], i ) );while( ! q.empty() ) {int now = q.top().second; q.pop();if( f[now] ) root[now] = merge( root[now], root[f[now]] );}int ans = 0;if( dis[1] <= En ) En -= dis[1], ans ++;if( root[1] ) q.push( make_pair( t[root[1]].val, root[1] ) );while( ! q.empty() ) {auto w = q.top().first;auto now = q.top().second;q.pop();if( dis[1] + w > En ) break;else ans ++, En -= dis[1] + w;if( t[now].lson ) //轉移一 替換最后一條邊q.push( make_pair( w - t[now].val + t[t[now].lson].val, t[now].lson ) );if( t[now].rson )q.push( make_pair( w - t[now].val + t[t[now].rson].val, t[now].rson ) ); if( root[t[now].End] ) //轉移二 找邊終點所有祖先的可擴展最小非樹邊 q.push( make_pair( w + t[root[t[now].End]].val, root[t[now].End] ) );}printf( "%d\n", ans );return 0; }

總結

以上是生活随笔為你收集整理的【学习笔记】左偏树的可持久化(【模板】k短路 / [SDOI2010]魔法猪学院)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。