考虑把树展开,单独把叶子节点拿出来
于是可以把控制点的,抽象成是在它叶子节点间连权值为的边
显然只用在子树的最左边的叶子节点和最右边的叶子节点的下一个节点连边(最后一个叶子节点的下一个节点为 ),跑最小生成树即可
正确性证明的话,设叶子节点的权值分别为,做差分,显然
正确性的话,感性理解一下吧QAQ,有理有据的感性理解
代码:
- //OwO
- #include <bits/stdc++.h>
- #define N 200010
- #define rep(i,x,y) for(i=x;i<=y;++i)
- #define add(x,y) g[x].push_back(y)
- #define ll long long
- #define pii pair<int,int>
- using namespace std;
-
- ll c[N];
- int dfn[N],sz[N],cnt,tot,fa[N],qwq[N],l[N],r[N],rr[N],n,lf=0;
- vector<int> g[N];
- struct ed{
- int u,v,p;
- ll w;
- bool operator <(const ed &x)const{ return w<x.w; }
- }e[N];
- void ADD(int u,int v,int p,ll w){ e[++cnt]=(ed){u,v,p,w}; }
-
- pii dfs(int x,int fa){
- pii lst;
- int pl=0;
- if(g[x].size()<=1 && x!=1) l[x]=r[x]=++lf;
- for(int i=0;i<g[x].size();++i){
- int to=g[x][i];
- if(to!=fa){
- pii tmp=dfs(to,x);
- l[x]=min(l[x],tmp.first);
- r[x]=max(r[x],tmp.second);
- }
- }
- return make_pair(l[x],r[x]);
- }
-
- int find(int x){ return (fa[x]==x)?x:fa[x]=find(fa[x]); }
-
- ll solve1(){
- int i;ll ans=0;
- rep(i,1,cnt){
- int u=find(e[i].u),v=find(e[i].v);
- if(u==v) continue;
- if(u>v) swap(u,v);
- fa[v]=u;
- ans+=e[i].w;
- }
- return ans;
- }
-
- void solve2(){
- int i,nxt,j;tot=0;
- rep(i,1,n+1) fa[i]=i;
- for(i=1;i<=cnt;i=nxt){
- for(j=i;e[j].w==e[i].w;++j){
- int u=find(e[j].u),v=find(e[j].v);
- if(u!=v) qwq[++tot]=e[j].p;
- }
- nxt=j;
- for(j=i;e[j].w==e[i].w;++j){
- int u=find(e[j].u),v=find(e[j].v);
- if(u>v) swap(u,v);
- if(u!=v) fa[v]=u;
- }
- }
- }
-
- int main(){
- int i,x,y;
- scanf("%d",&n);
- rep(i,1,n) scanf("%I64d",&c[i]),fa[i]=i;fa[n+1]=n+1;
- rep(i,1,n-1){
- scanf("%d%d",&x,&y);
- add(x,y),add(y,x);
- }
- memset(l,0x3f,sizeof(l));
- memset(r,0,sizeof(r));
- dfs(1,0);rr[1]=n+1;
- rep(i,1,n) ADD(l[i],r[i]+1,i,c[i]);
- sort(e+1,e+cnt+1);
- cout<<solve1()<<" ";
- solve2();
- sort(qwq+1,qwq+tot+1);
- cout<<tot<<endl;
- rep(i,1,tot) printf("%d ",qwq[i]);
- }