洛谷3783 SDOI2017 天才黑客(最短路+虚树+边转点+线段树优化建图)
成功又一次自闭了
怕不是猪国杀之后最自闭的一次
一看到最短路径。
我们就能推测这应该是个最短路题
现在考虑怎么建图
根据题目的意思,我们可以发现,在本题中,边与边之间存在一些转换关系,但是点与点之间并不存在。
那么我们考虑 边转点,点转边。
每一条边拆成两个点,之间连边权的边
新建一个起点\(S\),与\(1\)号点的出边所对应的入点连边
然后根据原图中一个点的入度和出度之间的关系建图。(我们可以将\(LCP\)视为\(trie\)树上的\(LCA\))
最后跑一遍\(dijkstra\),那么对于每个原图中的点的最短路,就相当于所有该点的入边的出点的\(dis\)的最小值。
这样朴素建图是\(O(n^2)\)的
显然是接受不了的。
我们考虑怎么优化这个过程
首先,对于一个点来说,我们只需要考虑包含它的边的\(d\),也就是说,我们可以考虑把这些点拎出来,然后建一颗虚树,那么对于当前点,只有这颗虚树上的点才是有用的。
对于一个点\(x\),他作为转折的贡献,当且仅当他成为两条边的\(d\)的\(LCA\)的时候,那么我们可以将它的每个儿子的子树出点向其他儿子的子树入点连边,表示可以他们之间的代价是\(deep[x]-1\)
哎?子树?貌似子树的\(dfs\)序好像是连续的?是不是可以线段树优化建图的\xyx
没错,我们建立两颗线段树,分别表示区间连单点,和 单点连区间。
让当前点的所有入边的对应点连接区间连单点的对应叶子节点。出边也是类似。
然后新建两个点,分别让区间连和连区间。最后再连接这两个点,这两个点之间的边权是\(deep[x]-1\)
这里需要注意的是,每次建立线段树的点,都要留到最后的\(dijkstra\),所以这里建议结构体封装来做,会比较清晰一些
具体就直接看代码吧.......
真的自闭
\(7.5k\)
细节特别的多
尤其是!要把各个结构体分开,不要顺手写错了
虚树记得自杀式遍历
开的数组要够大
上代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk makr_pair
#define ll long long
#define pa pair<long long,int>
#define int long long
#define index inde
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = 2e6+1e2;
const int maxm = 8e6+1e2;
int point[maxn],nxt[maxn],to[maxn];
int cnt,n,m;
int index;
int a[maxn],k;
int siz[maxn];
vector<int> in[101010],out[101010];
int deep[101010],f[101010][21];
int dnf[maxn];
int tt;
int pos[maxn];
void addedge(int x,int y,int w)
{
nxt[++cnt]=point[x];
to[cnt]=y;
point[x]=cnt;
}
void dfs(int x,int fa,int dep)
{
dnf[x]=++tt;
deep[x]=dep;
for (int i=point[x];i;i=nxt[i])
{
int p = to[i];
if (p==fa) continue;
f[p][0]=x;
dfs(p,x,dep+1);
}
}
void init()
{
for (int j=1;j<=20;j++)
for (int i=1;i<=k;i++)
f[i][j]=f[f[i][j-1]][j-1];
}
int go_up(int x,int d)
{
for (int i=0;i<=20;i++)
{
if ((1 << i) & d)
x=f[x][i];
}
return x;
}
int lca(int x,int y)
{
if (deep[x]>deep[y]) x=go_up(x,deep[x]-deep[y]);
else y=go_up(y,deep[y]-deep[x]);
if (x==y) return x;
for (int i=20;i>=0;i--)
{
if (f[x][i]!=f[y][i])
{
x=f[x][i];
y=f[y][i];
}
}
return f[x][0];
}
struct result{
int point[maxn],nxt[maxm],to[maxm];
int cnt;
int val[maxm];
int dis[maxn],vis[maxn];
priority_queue<pa,vector<pa>,greater<pa> > q;
void init()
{
cnt=0;
memset(point,0,sizeof(point));
}
void addedge(int x,int y,int w)
{
nxt[++cnt]=point[x];
to[cnt]=y;
val[cnt]=w;
point[x]=cnt;
}
void dijkstra(int s)
{
memset(dis,127/3,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[s]=0;
q.push(make_pair(0,s));
while (!q.empty())
{
int x = q.top().second;
q.pop();
if (vis[x]) continue;
vis[x]=1;
for (int i=point[x];i;i=nxt[i])
{
int p=to[i];
if (dis[p]>dis[x]+val[i])
{
dis[p]=dis[x]+val[i];
q.push(make_pair(dis[p],p));
}
}
}
}
};
result g;
struct segment{
int f[4*maxn],gg[4*maxn];
int point[maxn],nxt[maxm],to[maxm];
int dfn[maxn],back[maxn],leafout[maxn];
int leafin[maxn];
int tot=0;
int cnt=0;
int size[maxn];
void init(){tot=0;cnt=0;}
void addedge(int x,int y)
{
nxt[++cnt]=point[x];
to[cnt]=y;
point[x]=cnt;
}
void dfs(int x,int fa)
{
dfn[x]=++tot;
size[x]=1;
back[tot]=x;
for (int i=point[x];i;i=nxt[i])
{
int p = to[i];
if (p==fa) continue;
dfs(p,x);
size[x]+=size[p];
}
}
void buildout(int root,int l,int r)
{
f[root]=++index;
if (l==r)
{
leafout[l]=index;
return;
}
int mid = l+r >> 1;
buildout(2*root,l,mid);
buildout(2*root+1,mid+1,r);
g.addedge(f[2*root],f[root],0);
g.addedge(f[2*root+1],f[root],0);
}
void buildin(int root,int l,int r)
{
gg[root]=++index;
if (l==r)
{
leafin[l]=index;
return;
}
int mid = l+r >> 1;
buildin(2*root,l,mid);
buildin(2*root+1,mid+1,r);
g.addedge(gg[root],gg[2*root],0);
g.addedge(gg[root],gg[2*root+1],0);
}
void updateout(int root,int l,int r,int x,int y,int p)
{
if (x<=l && r<=y)
{
g.addedge(f[root],p,0);
return;
}
int mid = l+r >> 1;
if (x<=mid) updateout(2*root,l,mid,x,y,p);
if (y>mid) updateout(2*root+1,mid+1,r,x,y,p);
}
void updatein(int root,int l,int r,int x,int y,int p)
{
if (x<=l && r<=y)
{
g.addedge(p,gg[root],0);
return;
}
int mid = l+r >> 1;
if (x<=mid) updatein(2*root,l,mid,x,y,p);
if (y>mid) updatein(2*root+1,mid+1,r,x,y,p);
}
void link(int x,int y,int xx,int yy,int w)
{
if (x>y || xx>yy) return;
updateout(1,1,tot,x,y,index+1);
updatein(1,1,tot,xx,yy,index+2);
g.addedge(index+1,index+2,w);
index+=2;
}
void solve(int x)
{
dfs(1,0);
buildout(1,1,tot);buildin(1,1,tot);
for (int i=0;i<in[x].size();i++) g.addedge(in[x][i]+m,leafout[dfn[pos[in[x][i]]]],0);
for (int i=0;i<out[x].size();i++) g.addedge(leafin[dfn[pos[out[x][i]]]],out[x][i],0);
for (int i=1;i<=tot;i++)
{
int now = back[i];
link(dfn[now],dfn[now],dfn[now],dfn[now]+size[now]-1,deep[now]-1);
for (int &j=point[now];j;j=nxt[j])
{
int p = to[j];
link(dfn[p],dfn[p]+size[p]-1,dfn[now],dfn[p]-1,deep[now]-1);
link(dfn[p],dfn[p]+size[p]-1,dfn[p]+size[p],dfn[now]+size[now]-1,deep[now]-1);
}
}
}
};
segment tree;
bool cmp(int a,int b)
{
return dnf[a]<dnf[b];
}
struct xvshu{
int cnt;
int top;
int s[maxn];
void solve(int x)
{
tree.init();
k=0;
for (int i=0;i<in[x].size();i++) a[++k]=pos[in[x][i]];
for (int i=0;i<out[x].size();i++) a[++k]=pos[out[x][i]];
sort(a+1,a+1+k,cmp);
cnt=0;
top=1;
s[top]=1;
for (int i=1;i<=k;i++)
{
if (a[i]==a[i-1]) continue;
int l = lca(a[i],s[top]);
if(l!=s[top])
{
while (top>1)
{
if (dnf[s[top-1]]>dnf[l])
{
tree.addedge(s[top-1],s[top]);
top--;
}
else
{
if(dnf[s[top-1]]==dnf[l])
{
tree.addedge(s[top-1],s[top]);
top--;
break;
}
else
{
tree.addedge(l,s[top]);
s[top]=l;
break;
}
}
}
}
if (s[top]!=a[i]) s[++top]=a[i];
}
while (top>1)
{
tree.addedge(s[top-1],s[top]);
top--;
}
}
};
xvshu xv;
void clear()
{
tree.init();
tt=0;
cnt=0;
memset(point,0,sizeof(point));
for (int i=1;i<=n;i++) in[i].clear(),out[i].clear();
g.init();
tree.init();
}
int t;
signed main()
{
cin>>t;
while (t--)
{
clear();
n=read(),m=read(),k=read();
index=2*m;
for (int i=1;i<=m;i++)
{
int x=read(),y=read(),w=read();
pos[i]=read();
g.addedge(i,i+m,w);
out[x].push_back(i);
in[y].push_back(i);
}
for (int i=1;i<k;i++)
{
int u=read(),v=read(),w=read();
addedge(u,v,w);
addedge(v,u,w);
}
dfs(1,0,1);
init();
for (int i=1;i<=n;i++)
{
if (in[i].size()==0 || out[i].size()==0) continue;
xv.solve(i);
tree.solve(i);
}
++index;
for (int i=0;i<out[1].size();i++)
{
g.addedge(index,out[1][i],0);
}
g.dijkstra(index);
for (int i=2;i<=n;i++)
{
int mn = 1e18;
for (int j=0;j<in[i].size();j++)
{
mn=min(mn,g.dis[in[i][j]+m]);
}
cout<<mn<<"\n";
}
}
return 0;
}