[atAGC025E]Walking on a Tree
设第$i$条边被$c_{i}$条路径覆盖,显然答案上界为$\sum\min(c_{i},2)$
事实上,上界可以被取到,考虑以下构造——
取树上的一个叶子,假设其到父亲的边为$i$,对其分类讨论:
1.若$c_{i}<2$,显然这条边总会产生$c_{i}$的贡献,不妨将该叶子删除
2.若$c_{i}\ge 2$,任取其中两条路径,并钦定者两条路径经过第$i$条边不同向
此时,两者公共部分均已达成,且限制等效于将其余两端点重新组成一条路径
重复上述过程即可,暴力实现的时间复杂度为$o(n^{2}+nm)$
瓶颈在于找路径和覆盖,前者可以启发式合并,后者可以树上差分
时间复杂度降为$o(n+m\log m)$,可以通过
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 100005 4 int n,x,y,sum,dep[N],dfn[N],tag[N],fa[N][20]; 5 int m,X[N<<1],Y[N<<1],XX[N],YY[N],bl[N<<1],ans[N<<1]; 6 vector<int>e[N],v[N]; 7 struct Union{ 8 int V,fa[N<<1]; 9 void init(int n){ 10 V=n; 11 for(int i=1;i<=n;i++)fa[i]=i; 12 } 13 int find(int k){ 14 return (k==fa[k] ? k : fa[k]=find(fa[k])); 15 } 16 void merge1(int x,int y){ 17 if (v[x].size()<v[y].size())swap(x,y); 18 for(int i:v[y])v[x].push_back(i); 19 fa[y]=x,tag[x]+=tag[y],v[y].clear(); 20 } 21 int merge2(int x,int y){ 22 V++,fa[x]=fa[y]=fa[V]=V; 23 return V; 24 } 25 }f1,f2; 26 int lca(int x,int y){ 27 if (dep[x]<dep[y])swap(x,y); 28 for(int i=19;i>=0;i--) 29 if (dep[fa[x][i]]>=dep[y])x=fa[x][i]; 30 if (x==y)return x; 31 for(int i=19;i>=0;i--) 32 if (fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i]; 33 return fa[x][0]; 34 } 35 void dfs(int k,int f,int s){ 36 fa[k][0]=f,dep[k]=s; 37 for(int i=1;i<20;i++)fa[k][i]=fa[fa[k][i-1]][i-1]; 38 for(int i:e[k]) 39 if (i!=f)dfs(i,k,s+1); 40 dfn[++dfn[0]]=k; 41 } 42 int main(){ 43 scanf("%d%d",&n,&m); 44 for(int i=1;i<n;i++){ 45 scanf("%d%d",&x,&y); 46 e[x].push_back(y),e[y].push_back(x); 47 } 48 dfs(1,0,1); 49 for(int i=1;i<=m;i++){ 50 scanf("%d%d",&X[i],&Y[i]); 51 XX[i]=X[i],YY[i]=Y[i]; 52 v[X[i]].push_back(i),v[Y[i]].push_back(i); 53 } 54 f1.init(n),f2.init(m); 55 for(int i=1;i<n;i++){ 56 int k=f1.find(dfn[i]); 57 if (tag[k])sum+=2; 58 else{ 59 int lst=0; 60 while (!v[k].empty()){ 61 int j=f2.find(v[k].back()); 62 if (f1.find(X[j])!=f1.find(Y[j])){ 63 if (lst)break; 64 lst=j; 65 } 66 v[k].pop_back(); 67 } 68 if (lst)v[k].push_back(lst); 69 sum+=min((int)v[k].size(),2); 70 if (v[k].size()>1){ 71 x=f2.find(v[k].back()),v[k].pop_back(); 72 y=f2.find(v[k].back()),v[k].pop_back(); 73 X[x]=f1.find(X[x]),Y[x]=f1.find(Y[x]); 74 X[y]=f1.find(X[y]),Y[y]=f1.find(Y[y]); 75 int t=f2.merge2(x,y); 76 X[t]=(X[x]^Y[x]^k),Y[t]=(X[y]^Y[y]^k); 77 bl[x]=bl[y]=t,ans[x]=(Y[x]==X[t]),ans[y]=(X[y]==Y[t]); 78 int tx=lca(X[x],Y[x]),ty=lca(X[y],Y[y]); 79 int z=f1.find((dep[tx]>dep[ty] ? tx : ty)); 80 tag[k]++,tag[z]--; 81 if (tx==ty)tag[f1.find(lca(X[t],Y[t]))]++,tag[z]--; 82 } 83 } 84 f1.merge1(k,f1.find(fa[dfn[i]][0])); 85 } 86 printf("%d\n",sum); 87 for(int i=f2.V;i;i--) 88 if (bl[i])ans[i]^=ans[bl[i]]; 89 for(int i=1;i<=m;i++){ 90 if (ans[i])printf("%d %d\n",XX[i],YY[i]); 91 else printf("%d %d\n",YY[i],XX[i]); 92 } 93 return 0; 94 }