您的位置:首页 > 其它

【USACO JAN 09】安全路径(Dijkstra+线段树合并)

2017-07-23 17:36 176 查看

传送门

安全路径

I think

题意:给出一张由点1到各点最短路径唯一的图,求点1到各点不经过最短路径的最后一条边的最短路径长度。

算法:Dijkstra+线段树合并

思路:构造最短路径树,对于每一个点i,其答案路径长是i子树内一点e到i子树外的非i父亲节点j的路径长,加点e、j到点1的距离,减去点i到点1的距离,即dis[1][e]+dis[1][j]+dis[j][e]−dis[1][i]。那么我们对于每个节点e就建一颗权值线段树维护dis[1][e]+dis[1][j]+dis[j][e]好了,遍历到一个点时先合并子节点权值线段树,更新权值线段树,删除以自己为lca的路径(子树合并之后线该路径两端即在同一颗树上了),计算该点的答案路径长。

Code

#include<queue>
#include<vector>
#include<cstdio>
#include<iostream>
#define s second
using namespace std;
typedef pair<int,int> pii;
const int sn = 100000+5;
const int sm = 200000+5;
const int sz = sn*20;

int n,m,tot,ya,yb,lim,cnt;
int dis[sn],ex[sn],anc[sn],ans[sn];//dijkstra&并查集
int to[sm<<1],nxt[sm<<1],w[sm<<1],hd[sn];//处理图的边
int Rt[sn],Ls[sz],Rs[sz],Val[sz],stk[sz],tp;//处理权值线段树
int ff[sn],dep[sn],Fa[sn][18];//处理Lca
priority_queue<pii,vector<pii>,greater<pii> >q;
vector<int>Lc[sn];

template <typename T> void read(T &x) {
char ch=getchar();x=0;int f=1;
while(ch>'9'||ch<'0') { if(ch=='-')f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
x*=f;
}
void Swap(int &x,int &y) { int t=x;x=y;y=t; }
int Max(int x,int y) { return x>y?x:y; }
void Dijkstra(int st) {
for(int i=1;i<=n;++i) dis[i]=lim,anc[i]=i;//一并初始化并查集
dis[st]=0,q.push(make_pair(dis[st],st));
while(!q.empty()) {
pii t=q.top();q.pop();
if(ex[t.s])continue;//别忘了一个点只能入队一次
ex[t.s]=1;
for(int i=hd[t.s];i;i=nxt[i])
if(!ex[to[i]]&&dis[to[i]]>dis[t.s]+w[i]) {
dis[to[i]]=dis[t.s]+w[i],ff[to[i]]=t.s;
q.push(make_pair(dis[to[i]],to[i]));
}
}
}
int find(int x) {
if(anc[x]!=x) anc[x]=find(anc[x]);
return anc[x];
}
void del(int &x) {
stk[++tp]=x,Ls[x]=Rs[x]=Val[x]=0,x=0;
}
void Insert(int &rt,int l,int r,int p,int val) {
if(!rt) rt=(tp?stk[tp--]:++tot);
Val[rt]+=val;
if(l==r) { if(!Val[rt])del(rt); return; }
int m=(l+r)>>1;
if(p<=m) Insert(Ls[rt],l,m,p,val);
else Insert(Rs[rt],m+1,r,p,val);
if(!Ls[rt]&&!Rs[rt]&&!Val[rt]) del(rt);
}
int Query(int rt,int l,int r) {
if(l==r)return l;
int m=(l+r)>>1;
if(Val[Ls[rt]]) return Query(Ls[rt],l,m);
else return Query(Rs[rt],m+1,r);
}
int Merge(int x,int &y) {
if(!(x*y))return x+y;
Val[x]+=Val[y];
Ls[x] = Merge(Ls[x],Ls[y]);
Rs[x] = Merge(Rs[x],Rs[y]);
del(y);
return x;
}
int lca(int u,int v) {
if(dep[u]!=dep[v]) {
if(dep[u]<dep[v]) Swap(u,v);
for(int i=17;i>=0;--i)
if(dep[Fa[u][i]]>dep[v])
u=Fa[u][i];
u=Fa[u][0];
}
if(u==v)return u;
for(int i=17;i>=0;--i)
if(Fa[u][i]!=Fa[v][i])
u=Fa[u][i],v=Fa[v][i];
return Fa[u][0];
}
void Dfsa(int x,int fa) {
dep[x]=dep[fa]+1,Fa[x][0]=fa;
for(int i=1;i<=17;++i)
if(Fa[x][i-1])Fa[x][i]=Fa[Fa[x][i-1]][i-1];
else break;
for(int i=hd[x];i;i=nxt[i])
if(to[i]!=fa&&ff[to[i]]==x)
Dfsa(to[i],x);
}
void Dfsb(int x,int fa) {
ya=find(x);
for(int i=hd[x];i;i=nxt[i]) {
if(to[i]==fa||ff[to[i]]!=x)continue;
Dfsb(to[i],x);
yb=find(to[i]);anc[yb]=ya;
Rt[x]=Merge(Rt[x],Rt[to[i]]);
}
for(int i=hd[x];i;i=nxt[i])//更新权值线段树-加点
if(to[i]!=fa&&ya!=find(to[i])) {
Insert(Rt[x],1,lim,dis[to[i]]+w[i]+dis[x],1);
Lc[lca(x,to[i])].push_back(dis[to[i]]+w[i]+dis[x]);
}
for(int i=0;i<Lc[x].size();++i)//更新权值线段树-删点
Insert(Rt[x],1,lim,Lc[x][i],-1);
int tmp=Query(Rt[x],1,lim);
ans[x]=Val[Rt[x]]?tmp-dis[x]:-1;
}
int main() {
freopen("travel.in","r",stdin);
freopen("travel.out","w",stdout);
read(n),read(m);
for(int i=1,a,b,c;i<=m;++i) {
read(a),read(b),read(c),lim=Max(c,lim);
to[++tot]=b,nxt[tot]=hd[a],hd[a]=tot,w[tot]=c;
to[++tot]=a,nxt[tot]=hd[b],hd[b]=tot,w[tot]=c;
}
lim*=n<<1;
tot=
b616
0,Dijkstra(1);
tot=0,Dfsa(1,0);
Dfsb(1,0);
for(int i=1;i<n;++i)
printf("%d\n",ans[i+1]);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: