这些天一直在刷LCA的题目。。LCA的题目并不是很多,不过到现在为止还有几题没刷完,先做个小结。。
LCA一般用的主要有三种算法:
1、朴素算法,看两个结点的深度,深度大的沿父结点先往上走,知道两个结点的深度相同,然后同时向上走,直到他们两个点重合,这就是他们的LCA了。。这个算法简单,但是用的比较少。
2、LCA在线算法(ST算法),在线算法就是出现一个查询就查一个,LCA在线算法一般是向RMQ转化,首先先对整个树进行DFS,将遍历的结点按顺序几下,这样我们就生成了一个长度为(2*n-1)的欧拉序列(欧拉序列到底是什么意思我也不知道,只是知道他是按顺序遍历树生成的),与此同时,我们还顺便记录下了每个结点第一次出现在欧拉序列的位置pos,然后对这个欧拉序列进行RMQ(这里面存的是每一段深度最小的),查询时输入的是结点要注意把结点转化为欧拉序列中的位置。
3、LCA离线算法(tarjan),离线算法要先读入所有的查询,里面还要用到并查集,里面本质还不是很懂。。。
然后就是一些题目了
模板题。。。用在线和离线都写了一遍。。
也是模板题。。。三个点的公式就是dist[a+dist[b+dist[c]-dist[lca(a,b)]-dist[lca(a,c)]-dist[lca(b,c)];
HDU2874 Connections between cities
大体还是模板,这里还需要判断一个不连通的状况,一个比较好的处理方法是给每个树染一个不相同的颜色(不连通是因为有多个树),然后这样处理下tarjan算法就不会出问题了。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=10010;
const int MAXE=20010;
const int MAXC=1000010;
int fa[MAXN];
struct EDGE
{
int v,next;
int dis;
}edge[MAXE];
struct QUEST
{
int v,next;
int num;
}q[MAXC<<1];
template <class T>
inline void scan_d(T &ret) {
char c; ret=0;
while((c=getchar())<'0'||c>'9');
while(c>='0'&&c<='9') ret=ret*10+(c-'0'),c=getchar();
}
int head1[MAXN],head2[MAXN],size;
int dist[MAXN],lca[MAXC];
short anss[MAXC],anst[MAXC];
int vis[MAXN];
void init()
{
memset(head1,-1,sizeof(head1));
memset(head2,-1,sizeof(head2));
memset(dist,0,sizeof(dist));
memset(vis,0,sizeof(vis));
size=0;
}
void add_edge(int u,int v,int dis)
{
edge[size].v=v;
edge[size].dis=dis;
edge[size].next=head1[u];
head1[u]=size++;
}
void add_q(int u,int v,int i)
{
q[size].v=v;
q[size].num=i;
q[size].next=head2[u];
head2[u]=size++;
}
int find(int x)
{
return fa[x]==x?x:fa[x]=find(fa[x]);
}
void tarjan(int u,int mark)
{
fa[u]=u;
vis[u]=mark;
for(int i=head2[u];i!=-1;i=q[i].next)
{
int v=q[i].v;
if(vis[v]==mark)
{
lca[q[i].num]=find(v);
}
}
for(int i=head1[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(!vis[v])
{
dist[v]=dist[u]+edge[i].dis;
tarjan(v,mark);
fa[v]=u;
}
}
}
int main()
{
int n,m,i,c;
while(scanf("%d%d%d",&n,&m,&c)==3)
{
init();
int u,v,x;
while(m--)
{
scan_d(u);
scan_d(v);
scan_d(x);
add_edge(u,v,x);
add_edge(v,u,x);
}
size=0;
for(i=0;i<c;i++)
{
scan_d(u);
scan_d(v);
add_q(u,v,i);
add_q(v,u,i);
anss[i]=u;
anst[i]=v;
}
memset(lca,-1,sizeof(lca));
int mark=0;
for(i=1;i<=n;i++)
{
if(!vis[i])
tarjan(i,++mark);
}
for(i=0;i<c;i++)
{
if(lca[i]==-1)
printf("Not connected\n");
else
printf("%d\n",dist[anss[i]]+dist[anst[i]]-2*dist[lca[i]]);
}
}
return 0;
}HDU3078 Network
求两个点路径上第k大的点,求出两个点的lca,然后分别沿两个点向上走到LCA,并记录下走过了哪些点,然后排个序,然后输出就是。
/*************************************************************************
> File Name: 3078.cpp
> Author: tjw
> Mail:
> Created Time: 2014年10月08日 星期三 10时25分39秒
************************************************************************/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<stack>
#include<map>
using namespace std;
const int MAXN=80010;
int dep[MAXN*2],set[MAXN*2],fa[MAXN],rank[MAXN*2],num;
int val[MAXN];
int dp[MAXN*2][30];
bool vis[MAXN];
struct EDGE
{
int v,next;
}edge[MAXN*4];
int head[MAXN],size;
int Min(int a,int b)
{
return dep[a]<dep[b]?a:b;
}
void init()
{
memset(head,-1,sizeof(head));
memset(vis,0,sizeof(vis));
size=0;
}
void add_edge(int u,int v)
{
edge[size].v=v;
edge[size].next=head[u];
head[u]=size++;
}
bool cmp(int x,int y)
{
return x>y;
}
void dfs(int u,int deep,int fat)
{
vis[u]=1;
rank[u]=num;
set[num]=u;
dep[num++]=deep;
fa[u]=fat;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(vis[v])
continue;
dfs(v,deep+1,u);
set[num]=u;
dep[num++]=deep;
}
}
void RMQ_init()
{
int i,j;
for(i=0;i<num;i++)
dp[i][0]=i;
for(j=1;(1<<j)<num;j++)
{
for(i=0;i+(1<<j)<num;i++)
{
dp[i][j]=Min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
}
}
int RMQ(int l,int r)
{
int k=(int)(log(r-l+1.0)/log(2.0));
return Min(dp[l][k],dp[r-(1<<k)+1][k]);
}
int LCA(int l,int r)
{
if(l>r)
swap(l,r);
return set[RMQ(l,r)];
}
int k;
int ans[MAXN*2];
void path(int s,int u)
{
while(s!=u)
{
ans[k++]=val[u];
u=fa[u];
}
}
int main()
{
int n,q,i;
while(scanf("%d%d",&n,&q)==2)
{
for(i=1;i<=n;i++)
scanf("%d",&val[i]);
int u,v;
init();
for(i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
add_edge(u,v);
add_edge(v,u);
}
num=0;
dfs(1,1,-1);
int op,x,y;
RMQ_init();
while(q--)
{
scanf("%d%d%d",&op,&x,&y);
if(op==0)
{
val[x]=y;
continue;
}
u=LCA(rank[x],rank[y]);
k=0;
ans[k++]=val[u];
path(u,x);
path(u,y);
k--;
if(op>k+1)
{
printf("invalid request!\n");
continue;
}
sort(ans,ans+k+1,cmp);
printf("%d\n",ans[op-1]);
}
}
return 0;
}
POJ1986 Distance Queries
模板,他们说做这题之前还要看哪题才能理解题意,但是可以YY那个东西对这题来说是没什么用的(数据范围还是有用的,可以看1984)
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=40010;
const int MAXE=20010;
struct EDGE
{
int v,next;
int dis;
}edge[MAXN<<1];
struct QEUT
{
int v,next;
int num;
}q[MAXE];
int head1[MAXN],head2[MAXN],size;
bool vis[MAXN];
int dist[MAXN],lca[MAXE],anss[MAXE],anst[MAXN],fa[MAXN];
void init()
{
memset(vis,0,sizeof(vis));
memset(head1,-1,sizeof(head1));
memset(head2,-1,sizeof(head2));
memset(dist,0,sizeof(dist));
size=0;
}
void add_edge(int u,int v,int dis)
{
edge[size].v=v;
edge[size].dis=dis;
edge[size].next=head1[u];
head1[u]=size++;
}
void add_q(int u,int v,int i)
{
q[size].v=v;
q[size].num=i;
q[size].next=head2[u];
head2[u]=size++;
}
int find(int x)
{
return fa[x]==x?x:fa[x]=find(fa[x]);
}
void tarjan(int u)
{
fa[u]=u;
vis[u]=1;
for(int i=head2[u];i!=-1;i=q[i].next)
{
int v=q[i].v;
if(vis[v])
{
lca[q[i].num]=find(v);
}
}
for(int i=head1[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(!vis[v])
{
dist[v]=dist[u]+edge[i].dis;
tarjan(v);
fa[v]=u;
}
}
}
int main()
{
int n,m,i,k;
while(scanf("%d%d",&n,&m)==2)
{
init();
int u,v,c;
char op;
while(m--)
{
scanf("%d%d%d %c",&u,&v,&c,&op);
add_edge(u,v,c);
add_edge(v,u,c);
}
scanf("%d",&k);
size=0;
for(i=0;i<k;i++)
{
scanf("%d%d",&u,&v);
add_q(u,v,i);
add_q(v,u,i);
anss[i]=u;
anst[i]=v;
}
tarjan(1);
for(i=0;i<k;i++)
{
printf("%d\n",dist[anss[i]]+dist[anst[i]]-2*dist[lca[i]]);
}
}
}POJ2763 Housewife Wind
树状数组+LCA,把那一串欧拉序列看成树状数组维护的对象,每次改变走过一段路需要的时间的时候,将这次的时间和原来的时间做差,然后从1-pos[]+1(pos是两个点中大的那个点)加上改变的值,前面dfs顺便记录下这个点结束遍历的位置,然后1-那个位置减去改变的值就可以了。
/*************************************************************************
> File Name: poj2763.cpp
> Author: tjw
> Mail:
> Created Time: 2014年10月08日 星期三 17时31分31秒
************************************************************************/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<stack>
#include<map>
#define ll long long
using namespace std;
const int MAXN=100010;
int c[MAXN];
int set[MAXN<<1],dep[MAXN<<1],dist[MAXN],rank[MAXN],fa[MAXN];
struct EDGE
{
int v,next;
int dis;
}edge[MAXN<<1];
int head[MAXN],size;
bool vis[MAXN];
void init()
{
memset(vis,0,sizeof(vis));
memset(head,-1,sizeof(head));
memset(c,0,sizeof(c));
size=0;
}
void add_edge(int u,int v,int dis)
{
edge[size].v=v;
edge[size].dis=dis;
edge[size].next=head[u];
head[u]=size++;
}
int Min(int a,int b)
{
return dep[a]<dep[b]?a:b;
}
int lowbit(int x)
{
return x&(-x);
}
void add(int x,int val)
{
while(x<MAXN)
{
c[x]+=val;
x+=lowbit(x);
}
}
int sum(int x)
{
int ans=0;
while(x>0)
{
ans+=c[x];
x-=lowbit(x);
}
return ans;
}
int num;
int ed[MAXN];
void dfs(int u,int deep)
{
vis[u]=1;
rank[u]=num;
set[num]=u;
dep[num++]=deep;
for(int i=head[u];i!=-1;i=edge[i].v)
{
int v=edge[i].v;
if(vis[v])
return;
fa[v]=u;
dist[v]=dist[u]+edge[i].v;
dfs(v,deep+1);
set[num]=u;
dep[num++]=deep;
}
ed[u]=num;
}
int dp[MAXN<<1][20];
void RMQ_init()
{
int i,j;
for(i=0;i<num;i++)
dp[i][0]=i;
for(j=1;(1<<j)<num;j++)
{
for(i=0;i+(1<<j)<num;i++)
{
dp[i][j]=Min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
}
}
int RMQ(int l,int r)
{
int k=(int)(log(r-l+1.0)/log(2.0));
return Min(dp[l][k],dp[r-(1<<k)+1][k]);
}
int LCA(int l,int r)
{
if(l>r)
swap(l,r);
return set[RMQ(l,r)];
}
int u[MAXN],v[MAXN],val[MAXN];
int main()
{
int n,q,s,i;
while(scanf("%d%d%d",&n,&q,&s)==3)
{
init();
for(i=1;i<n;i++)
{
scanf("%d%d%d",&u[i],&v[i],&val[i]);
add_edge(u[i],v[i],val[i]);
add_edge(v[i],u[i],val[i]);
}
dist[1]=0;
fa[1]=-1;
dfs(1,1);
RMQ_init();
while(q--)
{
int op,x,y;
scanf("%d",&op);
if(op==0)
{
scanf("%d",&x);
int xx=rank[s],yy=rank[x];
int cc=LCA(xx,yy);
int z=rank[cc];
printf("%d\n",sum(xx+1)+sum(yy+1)-2*sum(cc+1)+dist[s]+dist[x]-2*dist[cc]);
s=x;
}
else
{kai shi
scanf("%d%d",&x,&y);
int xx=u[x];
int yy=v[x];
int temp=y-val[x];
val[x]=y;
if(fa[xx]==yy)
swap(xx,yy);
add(rank[yy]+1,temp);
add(ed[yy]+1,-temp);
}
}
}
return 0;
}POJ3694 Network
LCA朴素算法,先双连通缩点求桥,然后朴素算法沿这两个点向上跑,遇到桥就数量减1,然后把这个桥去掉。。
/*************************************************************************
> File Name: poj3964.cpp
> Author: tjw
> Mail:
> Created Time: 2014年10月08日 星期三 20时17分08秒
************************************************************************/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<stack>
#include<map>
#define ll long long
using namespace std;
const int MAXN=100010;
const int MAXE=200010;
struct EDGE
{
int v,next,uesd;
}edge[MAXE<<1];
int pre[MAXN],low[MAXN],bcc_cnt,fa[MAXN],dfs_clock;
int head[MAXN],size;
int vis[MAXN],bridge[MAXN];
void init()
{
memset(vis,0,sizeof(vis));
memset(head,-1,sizeof(head));
memset(bridge,0,sizeof(bridge));
dfs_clock=bcc_cnt=size=0;
}
void add_edge(int u,int v)
{
edge[size].v=v;
edge[size].uesd=0;
edge[size].next=head[u];
head[u]=size++;
}
void tarjan(int u)
{
pre[u]=low[u]=++dfs_clock;
vis[u]=1;
for(int i=head[u];i!=-1;i=edge[i].next)
{
if(!edge[i].uesd)
{
edge[i].uesd=edge[i^1].uesd=1;
int v=edge[i].v;
if(!vis[v])
{
fa[v]=u;
tarjan(v);
low[u]=min(low[u],low[v]);
if(pre[u]<low[v])
{
bcc_cnt++;
bridge[v]=1;
}
}
else if(vis[v]==1)
low[u]=min(low[u],pre[v]);
}
}
vis[u]=2;
}
void LCA(int u,int v)
{
if(pre[u]>pre[v])
swap(pre[u],pre[v]);
while(pre[v]>pre[u])
{
if(bridge[v])
{
bcc_cnt--;
bridge[v]=0;
}
v=fa[v];
}
while(u!=v)
{
if(bridge[u])
{
bcc_cnt--;
bridge[u]=0;
}
if(bridge[v])
{
bcc_cnt--;
bridge[v]=0;
}
u=fa[u];
v=fa[v];
}
}
int main()
{
int n,m,q,flag=1;
while(scanf("%d%d",&n,&m)==2)
{
if(n==0&&m==0)
break;
int u,v;
init();
while(m--)
{
scanf("%d%d",&u,&v);
add_edge(u,v);
add_edge(v,u);
}
tarjan(1);
scanf("%d",&q);
printf("Case %d:\n",flag++);
while(q--)
{
scanf("%d%d",&u,&v);
LCA(u,v);
printf("%d\n",bcc_cnt);
}
printf("\n");
}
return 0;
}POJ3417 Network
LCA+简单树形DP,对于每加一条边,就将这两个点和他们的lca组成的环上的边都加1,,然后判断每一条边,如果没有加1(就是0),可以把这条边删去加上任意一条新加的边,所以+=q,等于1的话,就一定要删去新加的边和当前边,所以+=1,大于1,无论怎么删都不可能,所以对两个点dp[u]+=1,dp[v]+=1,dp[lca]-=2,然后对整个树dfs从底部更新上来就可以了。
/*************************************************************************
> File Name: poj3417.cpp
> Author: tjw
> Mail:
> Created Time: 2014年10月10日 星期五 09时03分09秒
************************************************************************/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<stack>
#include<map>
#define ll long long
using namespace std;
const int MAXN=100010;
struct EDGE
{
int v,next;
}edge[MAXN<<1];
int head1[MAXN],head2[MAXN],size;
bool vis[MAXN];
int fa[MAXN],lca[MAXN];
struct QUEST
{
int v,next;
int num;
}q[MAXN<<1];
void init()
{
memset(vis,0,sizeof(vis));
memset(head1,-1,sizeof(head1));
memset(head2,-1,sizeof(head2));
size=0;
}
void add_edge(int u,int v)
{
edge[size].v=v;
edge[size].next=head1[u];
head1[u]=size++;
}
void add_q(int u,int v,int i)
{
q[size].v=v;
q[size].num=i;
q[size].next=head2[u];
head2[u]=size++;
}
int find(int x)
{
return fa[x]==x?x:fa[x]=find(fa[x]);
}
void tarjan(int u)
{
if(vis[u])
return;
vis[u]=1;
fa[u]=u;
for(int i=head2[u];i!=-1;i=q[i].next)
{
int v=q[i].v;
if(vis[v])
{
lca[q[i].num]=find(v);
}
}
for(int i=head1[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(!vis[v])
{
tarjan(v);
fa[v]=u;
}
}
}
int dp[MAXN];
void dfs(int u)
{
if(vis[u])
return;
vis[u]=1;
for(int i=head1[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(vis[v])
continue;
dfs(v);
dp[u]+=dp[v];
}
}
int x[MAXN],y[MAXN];
int main()
{
int n,m,i;
while(scanf("%d%d",&n,&m)==2)
{
init();
int u,v;
for(i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
add_edge(u,v);
add_edge(v,u);
}
size=0;
for(i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
add_q(u,v,i);
add_q(v,u,i);
x[i]=u;
y[i]=v;
}
memset(dp,0,sizeof(dp));
tarjan(1);
for(i=1;i<=m;i++)
{
u=x[i],v=y[i];
dp[u]++;
dp[v]++;
dp[lca[i]]-=2;
}
int ans=0;
memset(vis,0,sizeof(vis));
dfs(1);
for(i=2;i<=n;i++)
{
if(dp[i]==0)
ans+=m;
else if(dp[i]==1)
ans++;
}
printf("%d\n",ans);
}
return 0;
}
POJ3728 The merchant
这题总体感觉很复杂,,,先上码
/*************************************************************************
> File Name: poj3728.cpp
> Author: tjw
> Mail:
> Created Time: 2014年10月10日 星期五 14时48分39秒
************************************************************************/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<stack>
#include<map>
#define ll long long
using namespace std;
const int MAXN=50010;
int up[MAXN],down[MAXN],Max[MAXN],Min[MAXN];
int lca,val[MAXN];
int head1[MAXN],head2[MAXN],head3[MAXN],size,sz;
bool vis[MAXN];
int ans[MAXN];
struct EDGE
{
int v,next;
}edge[MAXN<<1];
struct QUEST
{
int u,v,next;
}q[MAXN<<1];
struct ANS
{
int v,next;
int num;
}e[MAXN<<1];
void init()
{
memset(head1,-1,sizeof(head1));
memset(head2,-1,sizeof(head2));
memset(head3,-1,sizeof(head3));
memset(vis,0,sizeof(vis));
size=sz=0;
}
void add_edge(int u,int v)
{
edge[size].v=v;
edge[size].next=head1[u];
head1[u]=size++;
}
void add_q(int u,int v)
{
q[size].u=u;
q[size].v=v;
q[size].next=head2[u];
head2[u]=size++;
}
void add_ans(int u,int k)
{
e[sz].next=head3[u];
e[sz].num=k;
head3[u]=sz++;
}
int fa[MAXN];
int update(int u)
{
if(fa[u]==u)
return u;
int par=fa[u];
fa[u]=update(fa[u]);
up[u]=max(up[u],max(up[par],Max[par]-Min[u]));
down[u]=max(down[u],max(down[par],Max[u]-Min[par]));
Max[u]=max(Max[u],Max[par]);
Min[u]=min(Min[u],Min[par]);
return fa[u];
}
void tarjan(int u)
{
if(vis[u])
return;
vis[u]=1;
fa[u]=u;
for(int i=head2[u];i!=-1;i=q[i].next)
{
int v=q[i].v;
if(vis[v])
{
lca=update(v);
add_ans(lca,i);
}
}
for(int i=head1[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(!vis[v])
{
tarjan(v);
fa[v]=u;
}
}
for(int i=head3[u];i!=-1;i=e[i].next)
{
int k=e[i].num;
int x=q[k].u,y=q[k].v;
if(k&1)
{
swap(x,y);
k=k^1;
}
k/=2;
update(x);
update(y);
ans[k]=max(up[x],down[y]);
ans[k]=max(ans[k],Max[y]-Min[x]);
}
}
int main()
{
int n,q,i;
while(scanf("%d",&n)==1)
{
init();
for(i=1;i<=n;i++)
{
scanf("%d",&val[i]);
up[i]=down[i]=0;
Max[i]=Min[i]=val[i];
}
int u,v;
for(i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
add_edge(u,v);
add_edge(v,u);
}
scanf("%d",&q);
size=0;
for(i=0;i<q;i++)
{
scanf("%d%d",&u,&v);
add_q(u,v);
add_q(v,u);
}
tarjan(1);
for(i=0;i<q;i++)
{
printf("%d\n",ans[i]);
}
}
return 0;
}
5万+

被折叠的 条评论
为什么被折叠?



