题目描述:https://www.luogu.org/problemnew/show/P4180
解析:首先考虑如何求不严格次小生成树,即所求生成树的边权总和大于等于最小生成树的边权总和。发现不严格次小生成树一定由最小生成树上的边替换掉某条边而得,于是想到枚举非树边。发现一条非树边(u,v)只能替换掉最小生成树上路径(u,v)中的最大值。于是问题边转换成了如何求树上路径的最大值,用树剖即可。(笔者水平太菜,不会写倍增)。
其次考虑怎么求严格次小生成树。考虑怎么维护一段区间的次大值,发现如果这段区间左区间的最大值大于右区间的最大值,那么这段区间的次大值就是左区间的次大值和右区间的最大值取max,反之亦然。如果这段区间左区间的最大值等于右区间的最大值,那么这段区间的次大值就是左区间的次大值和右区间的次大值取max。于是想到用pair来存最大值与次大值。
细节:1.这道题需要边转点,在最后树剖完当top[u]==top[v]时,区间查询不是查[finc[u],finc[v]],而是[finc[u]+1,finc[v]](finc为dfs序)。因为如果查的是[finc[u],finc[v]]的话,会将u点上面的路径一起计算,而这时不行的。2.记得最大值开1e14.
附上代码:
#include<cstdio> #include<algorithm> using namespace std; typedef long long LL; typedef pair<LL,LL> pii; const int MAXN=1000005,MAXM=3000005; int n,m; struct Edge{ int u,v; LL val; void init(){ scanf("%d%d%lld",&u,&v,&val); } }edge[MAXM]; int fa1[MAXN]; int vis[MAXM]; int head[MAXN]; struct Edg{ int to,last,val; }edg[2*MAXN]; int cnt=0; int fa[MAXN],size[MAXN],son[MAXN],dep[MAXN]; int inc[MAXN],finc[MAXN],top[MAXN]; int timer=0; LL v[MAXN]; int root=1; int lson[2*MAXN],rson[2*MAXN]; LL Max[2*MAXN],sec_Max[2*MAXN]; int ndnum=0; LL ans1=0,ans=1e14; bool cmp(Edge a,Edge b){ return a.val<b.val; } int find(int x){ if(x==fa1[x]) return x; else return fa1[x]=find(fa1[x]); } void Init(){ scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) edge[i].init(); for(int i=1;i<=n;i++) fa1[i]=i; sort(edge+1,edge+m+1,cmp); for(int i=1;;i++){ int u=edge[i].u,v=edge[i].v; int fa_u=find(u),fa_v=find(v); if(fa_u==fa_v) continue; vis[i]=1; ans1+=edge[i].val; cnt++; fa1[fa_u]=fa_v; if(cnt==n-1) break; } } void addedge(int u,int v,int val){ edg[++cnt].to=v; edg[cnt].last=head[u]; edg[cnt].val=val; head[u]=cnt; } void dfs1(int x,int f){ size[x]=1;fa[x]=f;dep[x]=dep[f]+1; for(int i=head[x];i;i=edg[i].last){ int y=edg[i].to; if(y==f) continue; v[y]=edg[i].val; dfs1(y,x); size[x]+=size[y]; if(size[y]>size[son[x]]||son[x]==0) son[x]=y; } } void dfs2(int x,int tp){ top[x]=tp;inc[++timer]=x;finc[x]=timer; if(son[x]) dfs2(son[x],tp); for(int i=head[x];i;i=edg[i].last){ int y=edg[i].to; if(y==fa[x]||y==son[x]) continue; dfs2(y,y); } } LL max(LL x,LL y){ return x>y?x:y; } void update(int t){ if(Max[lson[t]]>Max[rson[t]]){ Max[t]=Max[lson[t]]; sec_Max[t]=max(Max[rson[t]],sec_Max[lson[t]]); } else if(Max[lson[t]]<Max[rson[t]]){ Max[t]=Max[rson[t]]; sec_Max[t]=max(Max[lson[t]],sec_Max[rson[t]]); } else{ Max[t]=Max[lson[t]]; sec_Max[t]=max(sec_Max[lson[t]],sec_Max[rson[t]]); } } void buildtree(int &t,int l,int r){ t=++ndnum; if(l==r){ Max[t]=v[inc[l]]; sec_Max[t]=0; return; } int mid=(l+r)>>1; buildtree(lson[t],l,mid);buildtree(rson[t],mid+1,r); update(t); } void Prework(){ for(int i=1;i<=m;i++){ if(vis[i]){ int u=edge[i].u,v=edge[i].v,val=edge[i].val; addedge(u,v,val); addedge(v,u,val); } } dfs1(root,0); dfs2(root,root); buildtree(root,1,n); } LL min(LL a,LL b){ return a<b?a:b; } pii get_max(pii a,pii b){ LL res1,res2; if(a.first>b.first){ res1=a.first; res2=max(a.second,b.first); } else if(a.first<b.first){ res1=b.first; res2=max(b.second,a.first); } else{ res1=a.first; res2=max(a.second,b.second); } return make_pair(res1,res2); } pii query(int t,int l,int r,int L,int R){ if(L<=l&&r<=R) return make_pair(Max[t],sec_Max[t]); int mid=(l+r)>>1; pii res; if(L<=mid) res=get_max(res,query(lson[t],l,mid,L,R)); if(R>mid) res=get_max(res,query(rson[t],mid+1,r,L,R)); return res; } pii Query(int u,int v){ pii res; while(top[u]!=top[v]){ if(dep[top[u]]<dep[top[v]]) swap(u,v); res=get_max(res,query(root,1,n,finc[top[u]],finc[u])); u=fa[top[u]]; } if(dep[u]>dep[v]) swap(u,v); if(dep[u]!=dep[v]) res=get_max(res,query(root,1,n,finc[u]+1,finc[v])); return res; } void opt(int x){ int u=edge[x].u,v=edge[x].v; pii res=Query(u,v); if(edge[x].val>res.first){ LL anss=ans1; ans=min(ans,anss-res.first+edge[x].val); } else if(res.second){ LL anss=ans1; ans=min(ans,anss-res.second+edge[x].val); } } void work(){ for(int i=1;i<=m;i++) if(!vis[i]) opt(i); printf("%lld",ans); } int main(){ Init(); Prework(); work(); return 0; }