今天复习LCA专题,做了几道题
其一是codevs的2370-小机房的树极其裸的一道题
是不是很裸?直接上代码了。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define maxn 50010 int head[maxn],deep[maxn],dis[maxn]; int ecnt,n,f[maxn][35]; struct edge { int u,v,w; int next; }E[maxn<<1]; void addedge(int u,int v,int w) { E[++ecnt].u = u; E[ecnt].v = v; E[ecnt].w = w; E[ecnt].next = head[u]; head[u] = ecnt; } void dfs(int x,int fx) { deep[x] = deep[fx]+1; f[x][0] = fx; for(int i = head[x]; i ; i = E[i].next){ int v = E[i].v; if(v == fx) continue; dis[v] = dis[x] + E[i].w; dfs(v,x); } } void init() { for(int j = 1; (1<<j) <= n; j ++) for(int i = 1; i <= n; i ++) if(deep[i] >= (1<<j)) f[i][j] = f[f[i][j-1]][j-1]; } int getfather(int a,int b) { if(deep[a] < deep[b]) swap(a,b); int d = deep[a] - deep[b]; for(int j = 30; j >= 0; j --) if(d&1<<j) { a = f[a][j]; } if(a == b) return a; for(int j = 30; j >= 0 ; j --) if(f[a][j]!= f[b][j]) { a = f[a][j]; b = f[b][j]; } return f[a][0]; } int main() { scanf("%d",&n);int u,v,w,q; for(int i = 1; i < n; i ++) { scanf("%d%d%d",&u,&v,&w); u++;v++; addedge(u,v,w); addedge(v,u,w); } dfs(1,0); init(); scanf("%d",&q); while(q--) { scanf("%d%d",&u,&v); u++;v++; int fx = getfather(u,v); printf("%d ",dis[u]+dis[v] - 2*dis[fx]); } }
第二题也是codevs1036-商务旅行
这道题乍一看以为是最短路,但是它是树上的,所以我们可以直接求每两个点的LCA然后加起来就可以了。
#include<cstdio> #include<cstring> #include<algorithm> #define maxn 30010 using namespace std; int ecnt,head[maxn],deep[maxn],fa[maxn],top[maxn],size[maxn],son[maxn]; struct edge { int u,v,next; }E[maxn<<1]; void addedge(int u,int v) { E[++ecnt].u=u; E[ecnt].v=v; E[ecnt].next=head[u]; head[u]=ecnt; } void dfs(int x) { size[x]=1; for(int i=head[x];i;i=E[i].next) { int v=E[i].v; if(fa[x]==v)continue; fa[v]=x; deep[v]=deep[x]+1; dfs(v); size[x]+=size[v]; if(size[son[x]]<size[v])son[x]=v; } } void dfs2(int x,int tp) { top[x]=tp; if(son[x])dfs2(son[x],tp); for(int i=head[x];i;i=E[i].next) { int v=E[i].v; if(v==fa[x]||v==son[x])continue; dfs2(v,v); } } int lca(int x,int y) { while(top[x]!=top[y]) { if(deep[top[x]]<deep[top[y]])swap(x,y); x=fa[top[x]]; } return deep[x]>deep[y]?y:x; } int main() { int n,tmp,m,u,v,ans=0,a; scanf("%d",&n); for(int i=1;i<n;++i) { scanf("%d%d",&u,&v); addedge(u,v); addedge(v,u); } dfs(1);dfs2(1,1); scanf("%d%d",&m,&tmp); for(int i=2;i<=m;++i) { scanf("%d",&a); ans+=deep[tmp]+deep[a]-deep[lca(a,tmp)]*2; tmp=a; } printf("%d",ans); return 0; }
第三道题是bzoj的1787
这道题是求三个点的最近公共祖先,我们可以先求出每两个点的LCA k1,k2,k3然后比较,假设k1==k2那么肯定选k3为集结点,如果k1为集结点,那么肯定要有两个人从k3走过来,不满足最少的题意。有思路就很舒服了。
#include<cstdio> #include<algorithm> #include<cstring> #define maxn 500005 using namespace std; int ecnt,head[maxn],siz[maxn],son[maxn],deep[maxn],fa[maxn],top[maxn],n,m; struct edge{ int v,next; }E[maxn<<1]; void add(int u,int v) { E[++ecnt].v=v; E[ecnt].next=head[u]; head[u]=ecnt; } void dfs(int x) { siz[x]=1; for(int i=head[x] ; i ; i=E[i].next ) { int v=E[i].v; if(fa[x]==v)continue; deep[v]=deep[x]+1;fa[v]=x; dfs(v); siz[x]+=siz[v]; if(siz[son[x]]<siz[v])son[x]=v; } } void dfs2(int x,int tp) { top[x]=tp; if(son[x])dfs2(son[x],tp); for(int i=head[x] ; i ; i=E[i].next ) { int v=E[i].v; if(son[x]==v||fa[x]==v)continue; dfs2(v,v); } } int lca(int x,int y) { while(top[x]!=top[y]) { if(deep[top[x]]<deep[top[y]])swap(x,y); x=fa[top[x]]; } return deep[x]<deep[y]?x:y; } inline int read() { int ret(0); char ch=getchar(); while(ch>'9'||ch<'0')ch=getchar(); while(ch>='0'&&ch<='9') { ret=(ret<<1)+(ret<<3)+ch-'0'; ch=getchar(); } return ret; } int main() { int u,v,a,b,c,t1,t2,t3,jud,tmp,fu; n=read();m=read(); for(int i=1 ; i<n ; ++i ) { u=read();v=read(); add(u,v);add(v,u); } dfs(1);dfs2(1,1); while(m--) { a=read();b=read();c=read(); t1=lca(a,b);t2=lca(a,c);t3=lca(b,c); if(t1==t2) { fu=t3; jud=lca(t3,a); tmp=deep[b]+deep[c]+deep[a]-deep[t3]-deep[jud]*2; } if(t2==t3) { fu=t1; jud=lca(t1,c); tmp=deep[b]+deep[c]+deep[a]-deep[t1]-deep[jud]*2; } if(t1==t3) { fu=t2; jud=lca(t2,b); tmp=deep[b]+deep[c]+deep[a]-deep[t2]-deep[jud]*2; } printf("%d %d ",fu,tmp); } return 0; }