题意:
一颗无根树上有很多条简单路径,已知这些简单路径的两个端点,求最多能选择多少条路径,
使得任意两条路径没有交点。(交点指此点既在路径a上,又在路径b上)。
算法:
怎样来确定路径呢?首先从端点u走到端点v必定要经过u和v的lca,即lca(u,v)必定在以u、v为端点
的简单路径上。那么我们可以通过每条路径端点的lca的深度从大到小排序。
为了选择的个数更多,就要尽量避免冲突,故利用贪心的思想,从lca大的开始选择,每次选择后,
把路径上的所有点都标记。这样下次就不会选择到一条有重复点的路径了。
关于lca有离线的tarjan算法(dfs+并查集) 和在线的基于dfs+ rmq的st算法 的倍增算法。
个人觉得lca离线算法比较好理解。
算法主体
void tarjan(int x,int f)
{
fa[x] = x;
vis[x] = true; //有时可以不要,直接用lev[]数组标记走没走过
for(int i=0;i<q[u].size();i++)
{
int v = q[u][i]; //询问LCA(u,v)
if(vis[x]) lca[u][v] = lca[v][u] = root(v); //root(v)即找v集合的根节点
} //这里如果点的个数在100000的数量级或以上,二维的lca[][]是开不下的
//可以把第二个点v和询问的编号id作为pair放入q[u]数组中,这样就可以用lca[id] = root(v)记录了
for(int i=head[u];i!=-1;i=e[i].next)
{
int v = e[i].to;
if(v == f) continue;
if(!vis[v])
{
lev[v] = lev[u]+1;
tarjan(v);
fa[v] = u; //将v并入u集合
}
}
}
在线算法我学了一下午也没有特别深入的理解,我准备再学下rmq,这样再回头理解这个
LCA的在线算法。
我现在能理解的全部都注释到代码中了!
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<queue>
#define maxn 100010
using namespace std;
struct edge
{
int to,next;
}e[maxn<<1];
struct node
{
int u,v,lca;
}t[maxn];
int head[maxn],cnt,n;
int lev[maxn],fa[maxn][20];
bool vis[maxn];
bool cmp(node x,node y)
{
return lev[x.lca]>lev[y.lca];
}
void init()
{
memset(head,-1,sizeof(head));
cnt = 0;
}
void add(int x,int y)
{
e[cnt].to = y;
e[cnt].next = head[x];
head[x] = cnt++;
}
void bfs(int x)
{
queue<int> q;
lev[x] = 0;
fa[x][0] = x;
q.push(x);
while(!q.empty())
{
int u = q.front();
q.pop();
for(int i=1;i<20;i++)
fa[u][i] = fa[fa[u][i-1]][i-1];
for(int i=head[u];i!=-1;i=e[i].next)
{
int v = e[i].to;
if(v==fa[u][0]) continue;
fa[v][0] = u;
lev[v] = lev[u]+1;
q.push(v);
}
}
}
int LCA(int x,int y)
{
if(lev[x]<lev[y]) //如果x的深度比y的深度小则交换x和y
{
int tmp = x;
x = y;
y = tmp;
}
for(int i=19;i>=0;i--)
{
if((1<<i)&(lev[x]-lev[y])) //如果x和y不在同一深度,x往上移动到他的祖先节点
x = fa[x][i];
}
if(x==y) return x; //如果y在x到根节点之间的路径上,则lca(x,y) = y;
for(int i=19;i>=0;i--)
{
if(fa[x][i]!=fa[y][i]) //x和y到了同一深度后,继续往上移动到祖先节点
{ //直到两个的祖先节点相同则找到lca
x = fa[x][i];
y = fa[y][i];
}
}
return fa[x][0];
}
void dfs(int u)
{
vis[u] = true;
for(int i=head[u];i!=-1;i=e[i].next)
{
int v = e[i].to;
if(vis[v] || lev[v]<lev[u]) continue; //由于是无根树,所以只能把深度大于当前节点的子树中的点标记
dfs(v);
}
}
int main()
{
//freopen("1.txt","r",stdin);
int m,a,b;
while(scanf("%d%d",&n,&m)!=EOF)
{
init();
for(int i=1;i<n;i++)
{
scanf("%d%d",&a,&b);
add(a,b);
add(b,a);
}
bfs(1);
for(int i=0;i<m;i++)
{
scanf("%d%d",&t[i].u,&t[i].v);
t[i].lca = LCA(t[i].u,t[i].v);
}
sort(t,t+m,cmp);
memset(vis,0,sizeof(vis));
int ans = 0;
for(int i=0;i<m;i++)
{
int xx = t[i].u,yy = t[i].v,zz = t[i].lca;
if(!vis[xx] && !vis[yy])
{
ans++;
dfs(zz);
}
}
printf("%d\n",ans);
}
return 0;
}
//在线算法 LCA倍增算法
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#define maxn 100010
using namespace std;
struct edge
{
int to,next;
}e[maxn<<2];
struct node
{
int u,v,cs;
}t[maxn];
int cnt,head[maxn],lev[maxn],fa[maxn],n,lca[maxn];
bool vis[maxn];
vector<pair<int,int> > q[maxn];
void init()
{
for(int i=1;i<=n;i++)
q[i].clear();
cnt = 0;
memset(head,-1,sizeof(head));
}
void add(int x,int y)
{
e[cnt].to = y;
e[cnt].next = head[x];
head[x] = cnt++;
}
bool cmp(node x,node y)
{
return lev[x.cs]>lev[y.cs];
}
int root(int x)
{
if(fa[x]==x) return x;
else fa[x] = root(fa[x]);
return fa[x];
}
void dfs(int u,int f)
{
fa[u] = u;
pair<int,int> pp;
for(int i=0;i<q[u].size();i++)
{
pp = q[u][i];
int t1 = pp.first,t2 = pp.second;
if(lev[t1]==-1) continue;
t[t2].cs = root(t1);
}
for(int i=head[u];i!=-1;i=e[i].next)
{
int v = e[i].to;
if(v==f) continue;
lev[v] = lev[u]+1;
dfs(v,u);
fa[v] = u;
}
}
void dfs1(int x)
{
vis[x] = true;
for(int i=head[x];i!=-1;i=e[i].next)
{
int v = e[i].to;
if(vis[v] || lev[v]<lev[x]) continue;
dfs1(v);
}
}
int main()
{
//freopen("1.txt","r",stdin);
// freopen("11.txt","w",stdout);
int m,a,b;
while(scanf("%d%d",&n,&m)!=EOF)
{
init();
for(int i=1;i<n;i++)
{
scanf("%d%d",&a,&b);
add(a,b);
add(b,a);
}
for(int i=0;i<m;i++)
{
scanf("%d%d",&a,&b);
q[a].push_back(make_pair(b,i));
q[b].push_back(make_pair(a,i));
t[i].u = a;
t[i].v = b;
}
memset(lev,-1,sizeof(lev));
lev[1] = 0;
dfs(1,-1);
sort(t,t+m,cmp);
int ans = 0;
memset(vis,0,sizeof(vis));
for(int i=0;i<m;i++)
{
int xx = t[i].u,yy = t[i].v,zz = t[i].cs;
if(!vis[xx] && !vis[yy])
{
ans++;
dfs1(zz);
}
}
printf("%d\n",ans);
}
return 0;
}