题目大意
给定$n$个点$m$条边的无向图,有$K$个关键点,你要尽可能的让这些关键点两两匹配,使得所有点对之间可以通过简单路径连接且任意两个简单路径没有重复的边(可以是共同经过一个点),输出每一条这样的路径。
$Kleq n,mleq 5 imes 10^4$
题解
很显然对于每一个连通块,当且仅当它有奇数个关键点,那么它会有一个关键点没有被配对,否则一定可以满足两两配对。
考虑该联通块的任意一棵生成树,不考虑是否经过了同一条边,任意两两沿着树上简单路径染边。
若有$(a,b)cap (c,d) = (p,q)$其中$(x,y)$表示从$x,y$之间的简单路径(设$a,c$在$p$的同侧)。
那么只需要令$a,c$配对,$c,d$配对即可。
至于方案数,任意选择一棵生成树进行搜索即可,$G_x$表示以$x$为根的子树内没有配对的点是多少(若没有则$G_x=0$)。
枚举$x$的每个儿子$v$,若$G_v=0$就忽略,否则,若$G_x=0$,那么令$G_x=G_v$,不然就新建一条以$x$为$lca$的,一端是$G_x$一端是$G_v$的路径,再让$G_x=0$即可。
复杂度$O(n)$。
#include<bits/stdc++.h> #define LL long long #define M 100020 using namespace std; namespace IO{ const int BS=(1<<20)+5; int Top=0; char Buffer[BS],OT[BS],*OS=OT,*HD,*TL,SS[20]; const char *fin=OT+BS-1; char Getchar(){if(HD==TL){TL=(HD=Buffer)+fread(Buffer,1,BS,stdin);} return (HD==TL)?EOF:*HD++;} void flush(){fwrite(OT,1,OS-OT,stdout);} void Putchar(char c){*OS++ =c;if(OS==fin)flush(),OS=OT;} void write(int x){ if(!x){Putchar('0');return;} if(x<0) x=-x,Putchar('-'); while(x) SS[++Top]=x%10,x/=10; while(Top) Putchar(SS[Top]+'0'),--Top; } int read(){ int nm=0,fh=1; char cw=Getchar(); for(;!isdigit(cw);cw=Getchar()) if(cw=='-') fh=-fh; for(;isdigit(cw);cw=Getchar()) nm=nm*10+(cw-'0'); return nm*fh; } } using namespace IO; int n,K,m,fs[M],to[M],G[M],nt[M],fa[M],u[M],v[M],t[M],tot,tmp; int s1[M],s2[M]; bool vs[M]; #define link(a,b) nt[tmp]=fs[a],fs[a]=tmp,to[tmp++]=b #define ins(a,b,c) tot++,u[tot]=a,v[tot]=b,t[tot]=c void dp(int x){ G[x]=vs[x]?x:0; for(int i=fs[x];i!=-1;i=nt[i]){ if(fa[to[i]]) continue; fa[to[i]]=x,dp(to[i]); if(!G[to[i]]) continue; if(!G[x]) G[x]=G[to[i]]; else ins(G[x],G[to[i]],x),G[x]=0; } } void opt(int x,int y,int k){ int tp1=0,tp2=0; while(x!=k) s1[++tp1]=x,x=fa[x]; while(y!=k) s2[++tp2]=y,y=fa[y]; write(tp1+tp2),Putchar(' '); for(int i=1;i<=tp1;i++) write(s1[i]),Putchar(' '); write(k); for(int i=tp2;i>=1;i--) Putchar(' '),write(s2[i]); Putchar(' '); } int main(){ n=read(),m=read(),K=read(),memset(fs,-1,sizeof(fs)); for(int i=1;i<=m;i++){int x=read(),y=read();link(x,y),link(y,x);} for(int i=1;i<=K;i++) vs[read()]=true; for(int i=1;i<=n;i++) if(!fa[i]) fa[i]=i,dp(i); write(tot),Putchar(' '); for(int i=1;i<=tot;i++) opt(u[i],v[i],t[i]); flush(); return 0; }