洛谷P3639 [APIO2013] 道路费用 [生成树的特殊算法]
道路费用
格式难调,题面就不放了。
分析:
这是一道要细(yan)心(jing)的生成树的好(gui)题。
首先我们看到$k$的范围非常小,那么我们就可以直接$2^k$枚举每一条加边是否选择。然后我们再按权值大小依次加原边并且更新可以影响的加边的权值上限,再树形DP求解。但是这样复杂度是$O(2^k+NM\log M)$,需要考虑优化。
我们能发现,不管加边选不选,有一些边都一定会加入到生成树中,那么我们就把这些边提前连接,然后把产生出的联通块缩点,然后就能形成一个$k+1$个点的新图和$k$条加边和$k$条原边,再用上面的操作就行了。现在的复杂度是$2^k+M\log M$,可以接受了。
细节贼多,贼难调。。。
Code:(代码贼丑。。。)
//It is made by HolseLee on 3rd Oct 2018 //Luogu.org P3639 #include<bits/stdc++.h> #define inf 0x3f3f3f3f using namespace std; typedef long long ll; const int N=1e5+7, M=3e5+7; int n,m,K,sta,top,ag[25],fa[N],fa2[N]; int mn[N],dep[N],head[N],p[N],cnte; ll val[N],sum[N],ans; struct Edge { int to,nxt; Edge() {} Edge(const int _x,const int _y): to(_x),nxt(_y) {} }edge[M]; struct Node { int u,v,w; bool operator < (const Node x) const { return w < x.w; } }e[M],ne[25],q[M]; bool mark[M]; inline int read() { char ch=getchar(); int num=0; bool flag=false; while( ch<'0' || ch>'9' ) { if( ch=='-' ) flag=true; ch=getchar(); } while( ch>='0' && ch<='9' ) { num=num*10+ch-'0'; ch=getchar(); } return flag ? -num : num; } int find(int x) { return fa[x]==x ? x : fa[x]=find(fa[x]); } int find2(int x) { return fa2[x]==x ? x : fa2[x]=find2(fa2[x]); } inline void add(int x,int y) { edge[++cnte]=Edge(y,head[x]); head[x]=cnte; edge[++cnte]=Edge(x,head[y]); head[y]=cnte; } void get(int x) { sum[x]=val[x]; for(int i=head[x]; i; i=edge[i].nxt) { if( edge[i].to==fa2[x] ) continue; dep[edge[i].to]=dep[x]+1; fa2[edge[i].to]=x; get(edge[i].to); sum[x]+=sum[edge[i].to]; } } inline void work() { cnte=0; for(int i=1; i<=K+1; ++i) { int hl=ag[i]; head[hl]=fa2[hl]=0; fa[hl]=hl;mn[hl]=inf; } int u,v,fu,fv; for(int i=1; i<=K; ++i) if( mark[i] ) { u=find(ne[i].u), v=find(ne[i].v); if( u==v ) return; fa[u]=v; add(ne[i].u,ne[i].v); } for(int i=1; i<=K; ++i) { u=find(q[i].u), v=find(q[i].v); if( u!=v ) fa[u]=v, add(q[i].u, q[i].v); } get(sta); for(int i=1; i<=K; ++i) { u=q[i].u, v=q[i].v; if( dep[u]>dep[v] ) swap(u,v); while( dep[v]>dep[u] ) mn[v]=min(mn[v],q[i].w), v=fa2[v]; while( u!=v ) { mn[v]=min(mn[v],q[i].w); mn[u]=min(mn[u],q[i].w); u=fa2[u], v=fa2[v]; } } ll now=0; for(int i=1; i<=K; ++i) if( mark[i] ) { u=ne[i].u, v=ne[i].v; if( dep[u]>dep[v] ) swap(u,v); now+=mn[v]*sum[v]; } if( ans<now ) ans=now; } void dfs(int x) { if( x>K ) { work(); return; } mark[x]=0; dfs(x+1); mark[x]=1; dfs(x+1); } int main() { n=read(), m=read(), K=read(); for(int i=1; i<=m; ++i) { e[i].u=read(), e[i].v=read(), e[i].w=read(); } sort(e+1,e+m+1); for(int i=1; i<=K; ++i) { ne[i].u=read(), ne[i].v=read(); } for(int i=1; i<=n; ++i) p[i]=read(), fa[i]=fa2[i]=i; for(int i=1; i<=K; ++i) fa[find(ne[i].u)]=find(ne[i].v); int u,v,fu,fv; for(int i=1; i<=m; ++i) { u=e[i].u, v=e[i].v; if( (fu=find(u))!=(fv=find(v)) ) { fa[fu]=fa[fv]; fa2[find2(u)]=fa2[find2(v)]; } } sta=find2(1); for(int i=1; i<=n; ++i) { val[fu=find2(i)]+=p[i]; if( fu==i ) ag[++ag[0]]=i; } for(int i=1; i<=K; ++i) { ne[i].u=find2(ne[i].u); ne[i].v=find2(ne[i].v); } for(int i=1; i<=m; ++i) { e[i].u=find2(e[i].u); e[i].v=find2(e[i].v); } for(int i=1; i<=m; ++i) { u=find2(e[i].u), v=find2(e[i].v); if( u!=v ) q[++top]=e[i], fa2[u]=v; } dfs(1); printf("%lld\n",ans); return 0; }
蒟蒻写博客不易,如果有误还请大佬们提出
如需转载,请署名作者并附上原文链接,蒟蒻非常感激
名称:HolseLee
博客地址:www.cnblogs.com/cytus
个人邮箱:1073133650@qq.com
如需转载,请署名作者并附上原文链接,蒟蒻非常感激
名称:HolseLee
博客地址:www.cnblogs.com/cytus
个人邮箱:1073133650@qq.com