天才黑客
題解
貌似挺簡單的一道dijkstra
其實到最後發現就是建圖跑一次dijk,就是建圖有些噁心而已。
我們發現這道題本質上就是一個找最短路,關於每條邊的距離它給了一個trie樹來形容。
由於每條邊的長度會受到它當前所帶的字符串的影響,所以我們就順理成章的想到了拆點。
明顯不能把一個點拆成它所有的入邊個點,看了看數據範圍,空間炸了。
於是我們考慮另一種建圖方式,來拆邊:
我們可以把一個邊拆成2個點,分別爲它的入點與出點,它們之間連代表這條邊邊權的邊。對於兩條不同的邊,若他們可以到達,及他們爲(a,b)與(b,c),那麼我們就在它們直接連一條爲它們lcp的邊。最後從超源向點1連接了的邊連邊即可。統計答案時就統計所有能到這個點的邊的最小值即可。
不過這種圖還是會被菊花圖給炸掉。條邊,不炸纔怪。
我們貌似還得想辦法優化。由於這是一個trie樹上的字符串,當時一定會出現許多重複邊權的邊,因爲最多隻會種邊權。
好像還可以在trie樹上下功夫。於是我們按dfs序將每個邊排個序,像後綴數組一樣,它們中相鄰兩個點的lcp長度就是他們的height數組。那麼lcp就成了區間的最小值。這個最小值通過單調棧與線段樹很簡單就可以求出來了。
可貌似這種的方法還可以通過前綴和與後綴和來優化。
我們還可以不用單調棧來處理這個height數組。
因爲對於任何一個i某側的入點都可以以不超過的代價來的i另一側的出點,所以我們就可以讓兩邊的出入點連一條邊權爲的邊,很明顯這樣就可以用極少的邊完成連邊的工作了。
但這樣就不支持同時處理從左向右連與從右向左連。
我們可以把一條邊拆成4個點,分別處理兩種連邊。然後就可以建圖跑一邊dijk就溜了
源碼
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-fwhole-program")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-fstrict-overflow")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-skip-blocks")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fhoist-adjacent-loads")
#pragma GCC optimize("-frerun-cse-after-loop")
#pragma GCC optimize("inline-small-functions")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fexpensive-optimizations")
#pragma GCC optimize("-funsafe-loop-optimizations")
#pragma GCC optimize("inline-functions-called-once")
#pragma GCC optimize("-fdelete-null-pointer-checks")
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
using namespace std;
#define MAXN 20005
typedef long long LL;
#define int LL
#define gc() getchar()
const int INF=0x7f7f7f7f;
template<typename _T>
inline void read(_T &x){
_T f=1;x=0;char s=gc();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=gc();}
while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=gc();}
x*=f;
}
int t,n,m,k,dfn[MAXN],dep[MAXN];
namespace Tree{
const int N=2e4+5;
struct ming{
int v,w;
ming(){}
ming(int V,int W){v=V;w=W;}
friend bool operator < (ming x,ming y){return x.w<y.w;}
};
int f[N][20],cnt;vector<ming> trie[N];
void addEdge(int u,int v,int w){
trie[u].push_back(ming(v,w));
}
void dfs(int u){
for(int i=1;i<19;i++)f[u][i]=f[f[u][i-1]][i-1];
dfn[u]=++cnt;vector<ming>::iterator it;
for(it=trie[u].begin();it!=trie[u].end();++it)
dep[it->v]=dep[u]+1,f[it->v][0]=u,dfs(it->v);
}
void init(){
for(int i=1;i<=k;i++)sort(trie[i].begin(),trie[i].end());
dfs(1);
}
int lca(int a,int b){
if(dep[a]>dep[b])swap(a,b);
for(int i=18;i>=0;i--)if(dep[a]<=dep[f[b][i]])b=f[b][i];
if(a==b)return a;
for(int i=18;i>=0;i--)if(f[a][i]!=f[b][i])a=f[a][i],b=f[b][i];
return f[a][0];
}
void clear(){
for(int i=1;i<=k;i++)trie[i].clear();
memset(f,0,sizeof(f));cnt=0;
}
}
namespace Graph{
const int N=5e4+5,V=N<<2,E=20*N;
int tp[V],tott;
vector<int> eif[N],eof[N],eib[N],eob[N],tr;
int to[E],head[V],tot,nxt[E],paid[E],s;
bool cmp(int a,int b){return dfn[tp[a]]<dfn[tp[b]];}
void addEdge(int u,int v,int w){
to[++tot]=v;paid[tot]=w;
nxt[tot]=head[u];head[u]=tot;
}
void insert(int u,int v,int w,int d){
if(u==1)addEdge(s,tott+1,0),addEdge(s,tott+3,0);
for(int i=1;i<=4;i++)tp[tott+i]=d;
addEdge(tott+1,tott+2,w);addEdge(tott+1,tott+4,w);
addEdge(tott+3,tott+4,w);addEdge(tott+3,tott+2,w);
eof[u].push_back(++tott);eif[v].push_back(++tott);
eob[u].push_back(++tott);eib[v].push_back(++tott);
}
void build(){
for(int u=1;u<=n;u++){
sort(eof[u].begin(),eof[u].end(),cmp);
sort(eif[u].begin(),eif[u].end(),cmp);
sort(eob[u].begin(),eob[u].end(),cmp);
sort(eib[u].begin(),eib[u].end(),cmp);
int siz1=eof[u].size(),siz2=eif[u].size();
int siz3=eob[u].size(),siz4=eib[u].size();
for(int i=0;i<siz1-1;i++)addEdge(eof[u][i],eof[u][i+1],0);
for(int i=0;i<siz2-1;i++)addEdge(eif[u][i],eif[u][i+1],0);
for(int i=0;i<siz3-1;i++)addEdge(eob[u][i+1],eob[u][i],0);
for(int i=0;i<siz4-1;i++)addEdge(eib[u][i+1],eib[u][i],0);
tr.resize(eof[u].size()+eif[u].size());
merge(eif[u].begin(),eif[u].end(),eof[u].begin(),eof[u].end(),tr.begin(),cmp);
for(int t=0,i=0,j=0;t<tr.size()-1;t++){
(tr[t]&1)?j++:i++;int w=dep[Tree::lca(tp[tr[t]],tp[tr[t+1]])];
if(i!=0&&j!=eof[u].size())addEdge(eif[u][i-1],eof[u][j],w);
if(i!=eib[u].size()&&j!=0)addEdge(eib[u][i],eob[u][j-1],w);
}
}
}
struct ming{
int u,d;
ming(){}
ming(int U,int D){u=U;d=D;}
friend bool operator < (ming a,ming b){return a.d>b.d;}
};
int dis[V];bool vis[V];
priority_queue<ming> q;
void solve(){
for(int i=1;i<=tott;i++)dis[i]=(1LL<<40);dis[s]=0;
q.push(ming(s,0));
while(!q.empty()){
ming now=q.top();q.pop();
if(vis[now.u])continue;vis[now.u]=1;
for(int i=head[now.u];i;i=nxt[i]){
int v=to[i];if(vis[v])continue;
if(dis[v]>now.d+paid[i])
dis[v]=now.d+paid[i],q.push(ming(v,dis[v]));
}
}
for(int i=2;i<=n;i++){
int res=(1LL<<40),siz1=eif[i].size(),siz2=eib[i].size();
for(int j=0;j<siz1;j++)res=min(res,dis[eif[i][j]]);
for(int j=0;j<siz2;j++)res=min(res,dis[eib[i][j]]);
printf("%lld\n",res);
}
}
void clear(){
for(int i=1;i<=n;i++){
eif[i].clear();eof[i].clear();
eib[i].clear();eob[i].clear();
}
for(int i=1;i<=tott+1;i++)head[i]=0,vis[i]=false;
tot=tott=0;
}
}
signed main(){
read(t);
while(t--){
read(n);read(m);read(k);Graph::s=4*m+1;
for(int i=1;i<=m;i++){
int u,v,w,d;
read(u);read(v);read(w);read(d);
Graph::insert(u,v,w,d);
}
for(int i=1;i<k;i++){
int u,v,w;
read(u);read(v);read(w);
Tree::addEdge(u,v,w);
}
Tree::init();Graph::build();Graph::solve();
Tree::clear();Graph::clear();
}
return 0;
}