題目大意 :給定一個棵n個節點的樹,給定樹上m條路徑的兩個端點,求最多有多少條不相交的路徑(兩條路徑沒有公共頂點即爲不相交)。
基本思路:比賽時沒有做出來,沒想到這個路徑選擇有貪心性質,貪心思想比較巧妙。首先將這顆樹標記層次(我習慣從小到大),然後將每條路徑的兩個端點的最近公共祖先(也就等於是這條路徑在這個標記了層次的樹中層次最高的點)求出來,再按層次權值排序(將層次值最大的排在最前面),即可用貪心。每次選取一條路徑後,將這條路徑中的LCA點以下的所有點標記爲已訪問。
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 100010;
struct Edge
{
int u,v,f,w;
Edge(){}
Edge(int x,int y,int z,int xx)
{
u=x,v=y,f=z,w=xx;
}
}edges[maxn];
vector<int >G[maxn];
int fa[maxn][20],d[maxn];
int n,m,vis[maxn],depth;
bool cmp(const Edge&a,const Edge &b)
{
return a.w>b.w;
}
void dfs(int u)
{
for (int i=0;i<G[u].size();i++){
int v=G[u][i];
if (!d[v])
{
d[v]=d[u]+1,fa[v][0]=u;
dfs(v);
if (d[v]>depth)
depth=d[v];
}
}
}
void bfs (int u)
{
vis[u]=1;
for (int i=0;i<G[u].size();i++)
{
int v=G[u][i];
if (d[v]>d[u]&&!vis[v])
{
bfs(v);
}
}
}
int LCA(int p,int q)
{
int i,j;
if (d[p]<d[q])
{
int tem=p;
p=q;
q=tem;
}
for (i=0;(1<<i)<=d[p];i++);
for (j=i-1;j>=0;j--)
{
if (d[p]-(1<<j)>=d[q])
{
p=fa[p][j];
}
}
if (p==q)
return p;
for (j=i;j>=0;j--)
{
if (fa[p][j]!=fa[q][j])
{
p=fa[p][j];
q=fa[q][j];
}
}
return fa[p][0];
}
int main()
{
while (scanf("%d%d",&n,&m)!=EOF)
{
int i,j,u,v,ans=0;
depth=0;
memset(fa,-1,sizeof(fa));
for (i=1;i<=n;i++){
G[i].clear();
d[i]=0;
vis[i]=0;
}
for (i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
d[1]=1;
dfs(1);
for (j=1;(1<<j)<=depth;j++)
{
for (i=1;i<=n;i++)
{
int x=fa[i][j-1];
if (x!=-1)
{
fa[i][j]=fa[x][j-1];
}
}
}
for (i=0;i<m;i++){
scanf("%d%d",&u,&v);
int f=LCA(u,v);
edges[i]=Edge(u,v,f,d[f]);
}
sort(edges,edges+m,cmp);
for (i=0;i<m;i++)
{
if (!vis[edges[i].u]&&!vis[edges[i].v])
{
if (!vis[edges[i].f])
bfs(edges[i].f);
ans++;
}
}
printf("%d\n",ans);
}
return 0;
}