[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 } 
View Code

 

posted @ 2022-08-14 17:10  PYWBKTDA  阅读(41)  评论(0编辑  收藏  举报