Time Limits: 1000 ms Memory Limits: 131072 KB
Description
江湖由 N 个门派(2≤N≤100,000,编号从 1 到 N)组成,这些门派之间有 N-1 条小道将他们连接起来,每条道路都以“尺”为单位去计量,武林盟主发现任何两个门派都能够直接或者间接通过小道连接。
虽然整个江湖是可以互相到达的,但是他担心有心怀不轨之徒破坏这个武林的安定,破坏小道,于是武林盟主又秘密地修建了 M 条密道(1≤M≤100,000),但每条小道距离都不超过10亿尺。
果不其然,最近一个名叫“太吾”的组织意欲破坏武林的小道,请你帮盟主想想办法,如果门派 A 到门派 B 的直连小道被破坏,从 A 走到 B 的所有路径中,经过密道的距离最少是多少?
Input
第一行数字 N M
接下来 N-1 行,每行两个整数 A B,表示 A-B 间有一条直连小道
接下来 M 行,每行三个数字 A B V,表示 A-B 间有一条代价为 V 的密道
Output
输出 N-1 行,对应原输入的小道,每个小道被破坏后,最少需要经过多长的密道?如果不存在替换的道路,请输出-1
Sample Input
6 3
4 1
1 3
4 5
1 2
6 5
3 6 8
2 3 7
6 4 5
Sample Output
8
7
5
7
5
Data Constraint
30%数据:N<=300,M<=1000
50%数据:N<=1000,M<=1000
70%数据:N<=5000,M<=5000
对于另外15%的数据点:树是一条链
100%数据:N,M<=100,000
Solution
Algorithm 1
吐槽一下比赛查询的时候没有用
t
i
d
[
x
]
tid[x]
tid[x] 的心情。。。
100
100
100 变
0
0
0 ,心态崩了
这是一棵树,树上任意走代价为
0
0
0 ,删去任意一条小道,这棵树都会被分成两个连通块,一条密道(u,v)对某条边
(
x
,
y
)
(x,y)
(x,y) 有贡献当且仅当这条密道联通了被删去
(
x
,
y
)
(x,y)
(x,y) 边的后两边的连通块,容易发现,可以获得贡献的边为
u
u
u 和
v
v
v 到
l
c
a
(
u
,
v
)
lca(u,v)
lca(u,v) 上的边,这些边总是一段一段的,那么用树链剖分维护最小值即可。
时间复杂度
O
(
N
l
o
g
2
N
)
O(Nlog^2N)
O(Nlog2N)
Algorithm 2
极短极快!!!
将所有密道按权值从小到大排序。对于一条密道
(
u
,
v
,
w
)
(u,v,w)
(u,v,w) ,如果
u
u
u 到
v
v
v 路径上的边不曾被覆盖过,那么答案显然就是
w
w
w,可以使用并查集维护每个集合深度最小的结点,对于一条密道,每次
u
u
u 都在它所在的集合中找到深度最小的结点,这个点与其父亲的连边一定是上述的边,将这条边权值更新,并且与其父亲合并,直到
u
u
u 所在集合的深度最小的节点是小于
u
u
u 和
v
v
v 的
l
c
a
lca
lca 的,对
v
v
v 做同样的过程即可。
时间复杂度
O
(
M
α
(
N
)
)
O(M\alpha(N))
O(Mα(N))
Code
Algorithm 1
#include<algorithm>
#include<cstdio>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fd(i,a,b) for(int i=a;i>=b;--i)
#define L x<<1
#define R L|1
using namespace std;
const int N=1e5+5,inf=2147483647;
int n,m,num,cnt,len;
int last[N],tr[4*N],tag[4*N];
int son[N],dep[N],size[N],f[N][20];
int top[N],tid[N],rnk[N];
struct node{int x,y;}a[N];
struct edge{int to,next;}e[2*N];
void link(int x,int y) {e[++num]=(edge){y,last[x]},last[x]=num;}
void dfs1(int x,int fa)
{
f[x][0]=fa,dep[x]=dep[fa]+1;
for(int w=last[x];w;w=e[w].next)
{
int y=e[w].to;
if(y==fa) continue;
dfs1(y,x);
size[x]+=size[y];
if(son[x]<0||size[y]>size[son[x]]) son[x]=y;
}
}
void dfs2(int x,int tp)
{
rnk[++cnt]=x,tid[x]=cnt,top[x]=tp;
if(son[x]<0) return;
dfs2(son[x],tp);
for(int w=last[x];w;w=e[w].next)
{
int y=e[w].to;
if(y==f[x][0]||y==son[x]) continue;
dfs2(y,y);
}
}
int lca(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
fd(i,18,0) if(dep[f[x][i]]>=dep[y]) x=f[x][i];
if(x==y) return x;
fd(i,18,0) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
return f[x][0];
}
void build(int x,int l,int r)
{
tag[x]=inf;
if(l==r){tr[x]=inf; return;}
int mid=(l+r)>>1;
build(L,l,mid),build(R,mid+1,r);
}
void down(int x)
{
if(tag[x]==inf) return;
tr[L]=min(tr[L],tag[x]),tag[L]=min(tag[L],tag[x]);
tr[R]=min(tr[R],tag[x]),tag[R]=min(tag[R],tag[x]);
tag[x]=inf;
}
void change(int x,int st,int en,int l,int r)
{
if(st==l&&en==r)
{
tr[x]=min(tr[x],len),tag[x]=min(tag[x],len);
return;
}
int mid=(st+en)>>1; down(x);
if(r<=mid) change(L,st,mid,l,r);
else if(l>mid) change(R,mid+1,en,l,r);
else change(L,st,mid,l,mid),change(R,mid+1,en,mid+1,r);
}
void revise(int x,int l,int r,int p,int v)
{
if(l==r) {tr[x]=v; return;}
int mid=(l+r)>>1; down(x);
if(p<=mid) revise(L,l,mid,p,v);
else revise(R,mid+1,r,p,v);
}
int ask(int x,int l,int r,int p)
{
if(l==r) return tr[x];
int mid=(l+r)>>1; down(x);
if(p<=mid) return ask(L,l,mid,p);
else return ask(R,mid+1,r,p);
}
void find(int u,int v)
{
int f1=top[u],f2=top[v];
while(f1!=f2)
{
if(dep[f1]<dep[f2]) swap(f1,f2),swap(u,v);
change(1,1,n,tid[f1],tid[u]);
u=f[f1][0],f1=top[u];
}
if(dep[u]>dep[v]) swap(u,v);
change(1,1,n,tid[u],tid[v]);
}
int main()
{
freopen("worry.in","r",stdin);
freopen("worry.out","w",stdout);
scanf("%d%d",&n,&m);
fo(i,1,n-1) scanf("%d%d",&a[i].x,&a[i].y),link(a[i].x,a[i].y),link(a[i].y,a[i].x);
fo(i,1,n) size[i]=1,son[i]=-1;
dfs1(1,0),dfs2(1,1),build(1,1,n);
fo(j,1,18) fo(i,1,n) f[i][j]=f[f[i][j-1]][j-1];
fo(i,1,m)
{
int x,y;
scanf("%d%d%d",&x,&y,&len);
int z=lca(x,y),lst=ask(1,1,n,tid[z]);
find(x,z),find(y,z),revise(1,1,n,tid[z],lst);
}
fo(i,1,n-1)
{
if(dep[a[i].x]<dep[a[i].y]) swap(a[i].x,a[i].y);
int ans=ask(1,1,n,tid[a[i].x]);
printf("%d\n",ans==inf?-1:ans);
}
}
Algorithm 2
copy from ZZY
#include <cstdio>
#include <iostream>
#include <queue>
#include <cstring>
#include <algorithm>
#define N 100010
using namespace std;
struct edge{int to,from,w;}e[N*2];
struct node{int x,y,v;}Q[N];
int n,m,cnt,fa[N],dep[N],k[N],d[N],p[N],head[N],father[N];
bool cmp(node a,node b) { return a.v<b.v; }
void insert(int x,int y,int z)
{ e[++cnt].to=y; e[cnt].from=head[x]; e[cnt].w=z; head[x]=cnt; }
int getfather(int x)
{ return father[x]?father[x]=getfather(father[x]):x; }
void work(int &x,int y)
{ d[p[x]]=y,father[x]=getfather(fa[x]),x=father[x]; }
void dfs(int x,int pre)
{
fa[x]=pre,dep[x]=dep[pre]+1;
for (int i=head[x];i;i=e[i].from)
if (e[i].to!=pre) p[e[i].to]=e[i].w,dfs(e[i].to,x);
}
int main()
{
freopen("worry.in","r",stdin),freopen("worry.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1,x,y;i<n;i++) scanf("%d%d",&x,&y),insert(x,y,i),insert(y,x,i),d[i]=-1;
for (int i=1,x,y,z;i<=m;i++) scanf("%d%d%d",&Q[i].x,&Q[i].y,&Q[i].v);
dfs(1,0),sort(Q+1,Q+m+1,cmp);
for (int i=1;i<=m;i++)
{
int x=getfather(Q[i].x),y=getfather(Q[i].y);
while (x!=y) work(dep[x]<dep[y]?y:x,Q[i].v);
}
for (int i=1;i<n;i++) printf("%d\n",d[i]);
}