Ps:博主吐槽一下,那个多组数据要了博主半条命,白拍了半天啊。QAQ(大佬可无视)
【链接】
HDU6031
【题目大意】
给你一个n个节点的树,然后给你一个集合A和一个集合B,两个集合中分别有一些树的节点编号。让你从这两个集合中选出两个数x和y,使x,y的LCA的深度最大。
【解题报告】
从题目上我们可以想到用二分,但是二分分什么呢?分点与祖先的距离?好像不太行,时间复杂度太大。我们可以假设x,y的LCA为k,那么对于k的祖先节点都是x,y的公共祖先且这些点的深度小于k的深度,所以我们可以二分深度mid,求出A,B集合中的点的祖先深度是mid的,然后看这两个集合是否有交集。求祖先时运用倍增思想预先构造处fa数组,然后此题就解决了。
#include
#include
#include
using namespace std;
const int maxn=100005,maxm=200005,maxv=18;
int n,m,tot,L,R,len_a,len_b,MAX,a[maxn],b[maxn],dep[maxn],lnk[maxn],son[maxm],nxt[maxm],fa[maxn][maxv];
bool vis[maxn];
inline int Read()
{
int res=0;
char ch=getchar();
while (ch<'0'||ch>'9') ch=getchar();
while (ch>='0'&&ch<='9') res=res*10+ch-48,ch=getchar();
return res;
}
void Add(int x,int y)
{
son[++tot]=y; nxt[tot]=lnk[x]; lnk[x]=tot;
}
void Dfs(int x)
{
dep[x]=dep[fa[x][0]]+1; MAX=max(MAX,dep[x]);
for (int j=lnk[x]; j; j=nxt[j])
if (fa[x][0]!=son[j]) fa[son[j]][0]=x,Dfs(son[j]);
}
bool Check(int x)
{
set <int> S; S.clear();//这里用set判断交集比较方便简单
for (int i=1; i<=len_a; i++)
{
int d=dep[a[i]]-x,now=a[i];
if (d<0) continue;
for (int j=maxv-1; j>=0; j--)
if (d-(1<=0) now=fa[now][j],d-=1<if (now<0) continue;
S.insert(now);
}
for (int i=1; i<=len_b; i++)
{
int d=dep[b[i]]-x,now=b[i];
for (int j=maxv-1; j>=0; j--)
if (d-(1<=0) now=fa[now][j],d-=1<if (S.count(now)) return 1;
}
return 0;
}
void Work()
{
tot=MAX=0;
memset(lnk,0,sizeof(lnk));
for (int i=1,x,y; imemset(fa,255,sizeof(fa));
Dfs(1);
for (int j=1; jfor (int i=1; i<=n; i++)
if (fa[i][j-1]!=-1) fa[i][j]=fa[fa[i][j-1]][j-1];
for (int i=1; i<=m; i++)
{
len_a=Read();
for (int j=1; j<=len_a; j++) a[j]=Read();
len_b=Read();
for (int j=1; j<=len_b; j++) b[j]=Read();
L=1; R=MAX;
while (L<=R)
{
int mid=(R-L>>1)+L;
if (Check(mid)) L=mid+1; else R=mid-1;
}
printf("%d\n",R);
}
}
int main()
{
freopen("6031.in","r",stdin);
freopen("6031.out","w",stdout);
while (scanf("%d%d",&n,&m)==2) Work();
return 0;
}