Luogu P3783 [SDOI2017]天才黑客
題目大意
一道碼量直逼豬國殺的圖論+資料結構題。我豬國殺也就一百來行
首先我們要看懂鬼畜的題意,發現其實就是在一個帶權有向圖上,每條邊有一個字串資訊。讓你找一個點出發到其它點的最短路徑。聽起來很簡單,手速碼完Dijkstra
然而這題中除了路徑上的邊權和還要加上去的就是一條路徑上所有邊代表的字串的LCP,還有一點就是所有的字串都是一棵字典樹上的路徑。
問題有點複雜啊,那我們慢慢分析。
化邊為點
考慮到如果我們在跑最短路的時候記錄每個點過來的狀態就會不可避免的擴出\(O(n^2)\)條邊,直接GG
仔細一想這道題最難的部分在哪?其實還是求LCP部分,所以顯然邊的重要性比點大,因此我們化邊為點
具體的說,我們把原先的每條邊拆成一個入點一個出點,在這兩點之間連邊表示原來的邊權。
那麼現在我們只需要在這些邊化為的點直接連線邊權為兩字串LCP的邊即可(從一個出點連至一個入點)
構建虛樹
那麼現在我們就要找到原來每個點,將所有與其相連邊所需要的節點(不管是進入還是出去)之間的邊互相連線。
乍一看這樣的點數多的爆炸,但是仔細一想,總點數再怎麼多也不會超過\(2m\)啊!
所以我們對這些點構造一顆虛樹(不會虛樹的左轉Luogu P2495 [SDOI2011]消耗戰)
此時虛樹上的點就是它子樹內的點(包括他本身)之間的LCA,所以我們只要在這些點對之間連邊,邊權為這個點的\(dep-1\)
優化建邊
像前面說的那樣,直接兩兩暴力連邊邊數還是平方級別的。
我們發現這些點都要經過LCA,所以可以考慮以LCA為中轉點建虛點連邊。(不會建虛點的左轉Luogu P1983 車站分級)
然後每次連邊的邊數是\(O(n)\)的,但是有由於有多個點,所以總的邊數還是\(O(n^2)\)。優化了半天卻好似放屁
但是我們發現這個時候我們連的邊相當於是從某個子樹連到某個點,再從這個點連到某個子樹中
子樹的一個經典性質,相信我不說大家都知道:DFS序連續。
所以問題變成區間向點,點向區間連邊。這個由於我比較菜(不會前後綴優化建圖),同時這題的資料範圍不大,因此我們可以用最經典的線段樹優化建圖
那麼我們只需要同時維護一棵出線段樹和一棵入線段樹即可
總結&&CODE
最後問題迴歸到最短路問題,那麼這題就被我們輕鬆艱難的切掉了。
考慮複雜度,由於有線段樹的存在總邊數是\(O(n\log n)\)的(認為\(n,m\)同階)
算上Dijkstra的一個\(\log\)複雜度為\(O(n\log^2n)\)
千萬注意碼的時候要注意自己在幹什麼,不要不小心把自己碼暈了。
附上200+的CODE
// luogu-judger-enable-o2
#include<cstdio>
#include<cctype>
#include<queue>
#include<vector>
#include<cstring>
#include<algorithm>
#define RI register int
#define pb push_back
#define Ms(f,x) memset(f,x,sizeof(f))
#define Tp template <typename T>
using namespace std;
typedef long long LL;
const int N=50005,K=20005;
int t,n,m,k,dep[K],dfn[K],rv[2][K],tot;
class FileInputOutput
{
private:
#define S 1<<21
#define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
#define pc(ch) (Ftop<S?Fout[Ftop++]=ch:(fwrite(Fout,1,S,stdout),Fout[(Ftop=0)++]=ch))
char Fin[S],Fout[S],*A,*B; int Ftop,pt[25];
public:
Tp inline void read(T &x)
{
x=0; char ch; while (!isdigit(ch=tc()));
while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
}
Tp inline void write(T x)
{
if (!x) return (void)(pc('0'),pc('\n')); RI ptop=0;
while (x) pt[++ptop]=x%10,x/=10; while (ptop) pc(pt[ptop--]+48); pc('\n');
}
inline void Fend(void)
{
fwrite(Fout,1,Ftop,stdout);
}
#undef S
#undef tc
#undef pc
}F;
inline bool cmp(int x,int y)
{
return dfn[x]<dfn[y];
}
class LCA_Solver
{
private:
#define P 15
struct edge
{
int to,nxt;
}e[K]; int head[K],cnt,idx,anc[K][P],x,y,z;
inline void add(int x,int y)
{
e[++cnt]=(edge){y,head[x]}; head[x]=cnt;
}
inline void reset(int now)
{
for (RI i=0;i<P-1;++i) anc[now][i+1]=anc[anc[now][i]][i];
}
#define to e[i].to
inline void DFS(int now,int fa)
{
dep[now]=dep[fa]+1; reset(now); dfn[now]=++idx;
for (RI i=head[now];i;i=e[i].nxt) anc[to][0]=now,DFS(to,now);
}
#undef to
inline void swap(int &x,int &y)
{
int t=x; x=y; y=t;
}
public:
inline void init(void)
{
cnt=idx=0; Ms(head,0); for (RI i=1;i<k;++i)
F.read(x),F.read(y),F.read(z),add(x,y); DFS(1,0);
}
inline int query(int x,int y)
{
RI i; if (dep[x]<dep[y]) swap(x,y); for (i=P-1;~i;--i)
if (dep[anc[x][i]]>=dep[y]) x=anc[x][i]; if (x==y) return x;
for (i=P-1;~i;--i) if (anc[x][i]!=anc[y][i])
x=anc[x][i],y=anc[y][i]; return anc[x][0];
}
}L;
class Graph_Solver
{
private:
#define NS 1000005
#define MS 4000005
struct edge
{
int to,nxt,v;
}e[MS]; int head[NS],cnt,pos[NS],st,a,b,d,pcnt,p[N<<1]; bool vis[NS]; LL dis[NS],c;
int num[2][K<<2],stack[K],top,father[K],size[K],ndfn[K];;
struct data
{
LL val; int id;
inline data(LL Val=0,int Id=0) { val=Val; id=Id; }
inline friend bool operator <(data A,data B)
{
return A.val>B.val;
}
}; priority_queue <data> small; vector <int> v[N][2];
inline void add(int x,int y,int z)
{
e[++cnt]=(edge){y,head[x],z}; head[x]=cnt;
}
inline void build(int opt,int now,int l,int r)
{
num[opt][now]=++tot; if (l==r) return (void)(rv[opt][p[l]]=tot);
int mid=l+r>>1; build(opt,now<<1,l,mid); build(opt,now<<1|1,mid+1,r);
if (!opt) add(num[opt][now<<1],num[opt][now],0),add(num[opt][now<<1|1],num[opt][now],0);
else add(num[opt][now],num[opt][now<<1],0),add(num[opt][now],num[opt][now<<1|1],0);
}
#define O beg,end,id,val
inline void link(int opt,int now,int l,int r,int beg,int end,int id,int val)
{
if (l>=beg&&r<=end) { if (!opt) add(num[opt][now],id,val); else add(id,num[opt][now],val); return; }
int mid=l+r>>1; if (beg<=mid) link(opt,now<<1,l,mid,O); if (end>mid) link(opt,now<<1|1,mid+1,r,O);
}
#undef O
inline void VT_build(void)
{
RI i; father[stack[top=1]=p[1]]=0;
int t=pcnt; for (i=2;i<=t;++i)
{
int fa=L.query(stack[top],p[i]);
while (top&&dep[stack[top]]>dep[fa])
{
if (top==1||dep[stack[top-1]]<=dep[fa])
father[stack[top]]=fa; --top;
}
if (fa!=stack[top]) father[fa]=stack[top],stack[++top]=fa,p[++pcnt]=fa;
stack[++top]=p[i]; father[p[i]]=fa;
}
sort(p+1,p+pcnt+1,cmp); for (i=1;i<=pcnt;++i) size[p[i]]=1;
for (i=pcnt;i;--i) size[father[p[i]]]+=size[p[i]],ndfn[p[i]]=i;
for (build(0,1,1,pcnt),build(1,1,1,pcnt),i=1;i<=pcnt;++i)
{
int now=p[i]; ++tot; link(0,1,1,pcnt,i,i,tot,dep[now]-1);
link(1,1,1,pcnt,i,i+size[now]-1,tot,0); int fa=father[now]; if (!fa) continue;
++tot; link(0,1,1,pcnt,i,i+size[now]-1,tot,dep[fa]-1);
if (ndfn[fa]<=i-1) link(1,1,1,pcnt,ndfn[fa],i-1,tot,0);
if (i+size[now]<=ndfn[fa]+size[fa]-1) link(1,1,1,pcnt,i+size[now],ndfn[fa]+size[fa]-1,tot,0);
}
}
Tp inline void miner(T &x,T y)
{
if (y<x) x=y;
}
public:
inline void init(void)
{
RI i; for (i=1;i<=n;++i) v[i][0].clear(),v[i][1].clear();
for (cnt=tot=0,Ms(head,0),i=1;i<=m;++i)
{
F.read(a); F.read(b); F.read(c); F.read(d);
pos[tot+1]=d; pos[tot+2]=d; v[a][1].pb(tot+1); v[b][0].pb(tot+2);
add(tot+1,tot+2,c); tot+=2;
}
pos[st=++tot]=1; v[1][0].pb(st);
}
inline void build(void)
{
RI j,k; int lim; for (RI i=1;i<=n;++i)
{
for (pcnt=k=0;k<2;++k)
for (lim=v[i][k].size(),j=0;j<lim;++j)
p[++pcnt]=pos[v[i][k][j]];
sort(p+1,p+pcnt+1); pcnt=unique(p+1,p+pcnt+1)-p-1;
sort(p+1,p+pcnt+1,cmp); VT_build();
for (k=0;k<2;++k) for (lim=v[i][k].size(),j=0;j<lim;++j)
{
int t=v[i][k][j]; if (!k) add(t,rv[k][pos[t]],0); else add(rv[k][pos[t]],t,0);
}
}
}
#define to e[i].to
inline void Dijkstra(void)
{
Ms(dis,63); Ms(vis,0); small.push(data(dis[st]=0,st));
while (!small.empty())
{
int now=small.top().id; small.pop();
if (vis[now]) continue; vis[now]=1;
for (RI i=head[now];i;i=e[i].nxt)
if (dis[to]>dis[now]+e[i].v)
small.push(data(dis[to]=dis[now]+e[i].v,to));
}
}
#undef to
inline void print(void)
{
for (RI i=2;i<=n;++i)
{
int lim=v[i][0].size(); LL ans=1e18;
for (RI j=0;j<lim;++j) miner(ans,dis[v[i][0][j]]);
F.write(ans);
}
}
}G;
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (F.read(t);t;--t)
{
F.read(n); F.read(m); F.read(k); G.init();
L.init(); G.build(); G.Dijkstra(); G.print();
}
return F.Fend(),0;
}