SDOI2017 天才黑客 虛樹 最短路

2022-05-20 05:13:40 字數 3005 閱讀 3259

(原諒我寫不出簡單題意)

可以看到與\(trie\)樹上的字母以及\(lcp\)並沒有關係。。

以邊作為點,可以寫出乙個非常簡單的最短路\(dis_i=min \lbrace dis_j+dep_+c_i|v_j=u_i\rbrace\),還可以建立\(u_0=v_0=1,d_0=1,c_0=0\)作為原點

但是惡意卡的話有\(m^2\)條邊...

所以要考慮優化連邊

對於每個點\(x\),把所有\(u_i=x\)或\(v_i=x\)的\(d_i\)提出來,建出一棵虛樹,然後我們在虛樹上連邊

對於虛樹上的點,我們建立正點和反點,正點向虛樹上的父親連邊,反點向兒子連邊,邊權均為\(0\)

所有\(v_i=x\)的邊向虛樹上\(d_i\)的正點連一條為\(0\)的邊

所有\(u_i=x\)的邊,虛樹上\(d_i\)的反點連過來一條為\(c_i\)的邊

考慮\(lca\)的三種情況,在走到\(lca\)時將\(dep_\)考慮進來

1.對於\(lca(d_i,d_j)=d_i\)的情況,直接讓\(i\)向\(d_i\)的反點連一條權為\(dep_\)的邊,在反點間轉移即可

2.對於\(lca(d_i,d_j)=d_j\)的情況,同理的,讓\(d_j\)的正點向\(j\)連一條權為\(dep_\)的邊即可

3.最後一種情況比較複雜,因為我們要從\(lca\)走過去,考慮列舉\(lca\),讓轉移直接跳過\(lca\),在\(lca\)的兒子之間轉移。這樣能夠保證轉移無誤,但是如果直接連邊依然會被菊花圖卡到\(n^2\),如果我們建立乙個虛點連邊的話,會存在自己向自己轉移的非法情況。所以我們考慮字首字尾連邊,把所有兒子打成乙個序列\(son_i\),然後建立兩個虛點列\(a_i,b_i\),然後讓\(a_i\rightarrow a_,b_i \rightarrow b_\),\(son_i \rightarrow a_,son_i \rightarrow b_\),把其中\(son_i\)出發的邊邊權設為\(dep_\)即可

注意類似的連邊方式都要避免產生負邊,否則不能使用\(dijkstra\),會因為\(spfa\)而被卡掉..

#includeusing namespace std;

#define pb push_back

#define reg register

typedef long long ll;

#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)

#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)

char io;

int rd()

const int n=1e6+10,m=n/20+4;

const ll inf=1e18;

bool be;

int n,m,k;

struct edgee[m];

vector s[m],t[m];

void addedge(int u,int v,int c,int d,int id) );

t[v].pb(e[id]);

}int fa[m],dep[m],sz[m],top[m],son[m],l[m],r[m],dfn;

vector g[m];

typedef pairpii;

vector e[n];

void dfs(int u,int f) );

e[id[stk[top-1]]+1].pb((pii));

top--;

} if(stk[top]==lca)

if(dep[stk[top]]>dep[lca]) );

e[id[lca]+1].pb((pii));

top--;

stk[++top]=lca;

stk[++top]=x;

id[x]=++cnt;to[cnt]=to[cnt+1]=x;

cnt++; }}

void premake() );

e[id[stk[top-1]]+1].pb((pii));

top--;

} rep(i,0,t[u].size()-1) ); // 向正點連邊

e[t[u][i].id].pb((pii)); //直接向反點連邊,考慮dep

} rep(i,0,s[u].size()-1) ); //反點連過來

e[id[s[u][i].pos]].pb((pii));//直接從正點連過來

} rep(i,tcnt+1,cnt) if(~(i-tcnt)&1) );

if(~lst) );

e[cnt].pb((pii));

}lst=cnt;//連線ai

}lst=-1;

drep(j,lc,1) );

if(~lst) );

e[cnt].pb((pii));

}lst=cnt; //連線bi

}} }

}struct node

};priority_queue que;

ll dp[n],ans[m];

void dijkstra(int st) );

while(!que.empty()) );

} }rep(i,1,m) ans[e[i].to]=min(ans[e[i].to],dp[i]);

}bool ed;

int main()

rep(i,2,k)

dfn=0,dfs(1,0),dfs2(1,1);

cnt=m;

premake();

dijkstra(0);

fprintf(stderr,"%d\n",cnt);

rep(i,2,n) printf("%lld\n",ans[i]);

rep(i,0,cnt) e[i].clear();

rep(i,0,n) s[i].clear(),t[i].clear();

rep(i,0,k) g[i].clear();

}}

SDOI2017 天才黑客

我們不妨思考一下,兩個串的 lcp 的長度在 trie 樹上是什麼?對應點的 lca 的深度。然後就可以想到把邊看成點,然後把邊與邊之間連邊,點權看成這條邊經過所需要的時間。但是這樣子連邊是 o m 2 的,不夠優秀。我們需要進一步優化。考慮兩個點之間的 lcp 可以用什麼來代替?sa 裡面的 he...

SDOI2017 天才黑客

這題太神了。先模claris 大神的題解。首先我們要將邊轉換為點。如果暴力連邊就會有 m 2 的邊,於是我們考慮優化建圖。難點在於快速得到兩個邊的串的 lcp 也就是 trie 樹上的 lca 我們將一堆點按 dfs 序排序,然後 a 到 b 的 lca 就是排序後 min 這裡的 min 是深度最...

SDOI2017 天才黑客

題目大意 給一張有向圖,再給一顆字典樹,有向圖上的每條邊有乙個非負邊權還有乙個字典樹上的字串,從一條邊到另一條邊的代價是那條邊的邊權和這兩個字串的最長公共字首,問從1到其他點的最短路 題解一看肯定是乙個最短路問題,現在的關鍵問題是如何把這張圖建出來。我們可以列舉每個點作為兩條邊的中轉點,然後直接把每...