HDU3710 Battle over Cities(最小生成树+树链剖分+倍增+线段树)
Battle over Cities
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 467 Accepted Submission(s): 125
Given the map of cities which have all the destroyed and remaining highways marked, you are supposed to tell the cost to connect other cities if each city is conquered by the enemy.
Note: It is guaranteed that the whole country was connected before the war and there is no duplicated high ways between any two cities.
怎么预处理呢?
(1)连接子树之间的边。对于每条连接u,v没有在初始最小生成树里面的边,先求出u,v的lca,则这条边就是连接u的dep[u]-dep[lca]-1个father和v的dep[v]-dep[lca]-1个father的子树的边。用倍增求即可。
(2)连向父亲子树外面的边。对于每条连接u,v没有在初始最小生成树里面的边,先求出u,v的lca,则对于u的dep[u]-dep[lca]-2个father到u这条路径上的所有点,这条边都是连到它们父亲的子树外面的。注意,连向父亲子树外面的边只要取最小的一条即可,于是用倍增+树链剖分进行维护连向父亲子树外面的边的最短长度。对于v同理。
但为什么对每个点把所有可供选择的边预处理出来,再对这些边进行一次最小生成树不会超时呢?我们可以这样来想。因为一次Kruskal并查集的find操作是log(n)的,最坏情况下每条边都会用到一次find,因此重点是求出共有多少条边被用到。连接子树之间的边最多m-n条,连向子树外的边最多n条,所以总共进行m次find。对于m-n条不在最小生成树中的边,都进行预处理,一次预处理倍增是log(n)的,树剖是log(n)*log(n)的。因此,总时间复杂度为O(m log n+ m log n log n)。

#include<bits/stdc++.h> using namespace std; const int N=2e4+10; const int M=1e5+10; const int inf=2139062143; int T,n,m,ns,cnt,tim,head[N],to[N<<1],nxt[N<<1]; int dep[N],fa[N][20],siz[N],son[N],dfn[N],pos[N],top[N]; int minn[N],minv[N<<2],tag[N<<2]; bool use[M]; int s,mst,smst,pa[N],w[N],sont[N],num[N]; struct edge{ int u,v,d; bool operator < (const edge &x)const{return d<x.d;} } e[M]; vector<edge> link[N],edges; void Init() { smst=tim=cnt=0; memset(use,0,sizeof(use)); memset(head,0,sizeof(head)); memset(dep,0,sizeof(dep)); memset(fa,0,sizeof(fa)); memset(son,0,sizeof(son)); memset(sont,0,sizeof(sont)); memset(w,0,sizeof(w)); memset(minv,127,sizeof(minv)); memset(tag,127,sizeof(tag)); for(int i=1;i<=n;i++) pa[i]=i,link[i].clear(); } void AddEdge(int u,int v) { to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt; } int Find(int u){return u==pa[u]?u:pa[u]=Find(pa[u]);} void dfs1(int u) { for(int i=1;(1<<i)<=dep[u];++i) fa[u][i]=fa[fa[u][i-1]][i-1]; siz[u]=1; for(int i=head[u];i;i=nxt[i]) { int v=to[i]; if(v==fa[u][0]) continue; fa[v][0]=u; dep[v]=dep[u]+1; num[v]=++sont[u]; dfs1(v); siz[u]+=siz[v]; if(!son[u]||siz[son[u]]<siz[v]) son[u]=v; } } void dfs2(int u,int tp) { dfn[u]=++tim; pos[tim]=u; top[u]=tp; if(son[u]) dfs2(son[u],tp); for(int i=head[u];i;i=nxt[i]) { int v=to[i]; if(v!=fa[u][0]&&v!=son[u]) dfs2(v,v); } } void pushdown(int rt) { minv[rt<<1]=min(minv[rt<<1],tag[rt]); minv[rt<<1|1]=min(minv[rt<<1|1],tag[rt]); tag[rt<<1]=min(tag[rt<<1],tag[rt]); tag[rt<<1|1]=min(tag[rt<<1|1],tag[rt]); tag[rt]=inf; } void upd(int rt,int l,int r,int L,int R,int x) { if(L<=l&&R>=r) { minv[rt]=min(minv[rt],x); tag[rt]=min(tag[rt],x); return; } if(tag[rt]!=inf) pushdown(rt); int mid=(l+r)/2; if(L<=mid) upd(rt<<1,l,mid,L,R,x); if(R>mid) upd(rt<<1|1,mid+1,r,L,R,x); minv[rt]=min(minv[rt<<1],minv[rt<<1|1]); } void update(int u,int v,int x) { while(top[u]!=top[v]) { upd(1,1,n,dfn[top[u]],dfn[u],x); u=fa[top[u]][0]; } upd(1,1,n,dfn[v],dfn[u],x); } void getmin(int rt,int l,int r) { if(l==r) { minn[pos[l]]=minv[rt]; return; } if(tag[rt]!=inf) pushdown(rt); int mid=(l+r)/2; getmin(rt<<1,l,mid); getmin(rt<<1|1,mid+1,r); } void work(edge &e) { int u=e.u,v=e.v,d; d=dep[u]-dep[v]; for(int i=0;(1<<i)<=d;i++) if(d&(1<<i)) u=fa[u][i]; if(u==v) { u=e.u; d=dep[u]-dep[v]-2; if(d<0) return; for(int i=0;(1<<i)<=d;i++) if(d&(1<<i)) u=fa[u][i]; update(e.u,u,e.d); return; } int tmpu=u,tmpv=v; for(int i=15;i>=0;i--) { if(fa[tmpu][i]!=fa[tmpv][i]) { tmpu=fa[tmpu][i]; tmpv=fa[tmpv][i]; } } link[fa[tmpu][0]].push_back((edge){tmpu,tmpv,e.d}); d=dep[e.u]-dep[tmpu]-1; if(d>=0) { u=e.u; for(int i=0;(1<<i)<=d;i++) if(d&(1<<i)) u=fa[u][i]; update(e.u,u,e.d); } d=dep[e.v]-dep[tmpv]-1; if(d>=0) { v=e.v; for(int i=0;(1<<i)<=d;i++) if(d&(1<<i)) v=fa[v][i]; update(e.v,v,e.d); } } int main() { scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); Init(); int d,f; for(int i=1;i<=m;i++) { scanf("%d%d%d%d",&e[i].u,&e[i].v,&d,&f); e[i].d=d*(1-f); } sort(e+1,e+m+1); ns=0; for(int i=1;i<=m&&ns<n-1;i++) { int u=Find(e[i].u),v=Find(e[i].v); if(u!=v) { use[i]=true; pa[v]=u; ns++; AddEdge(e[i].u,e[i].v); AddEdge(e[i].v,e[i].u); w[e[i].u]+=e[i].d; w[e[i].v]+=e[i].d; smst+=e[i].d; } } dfs1(1); dfs2(1,1); for(int i=1;i<=m;i++) { if(!use[i]) { if(dep[e[i].u]<dep[e[i].v]) swap(e[i].u,e[i].v); work(e[i]); } } getmin(1,1,n); for(int i=1;i<=n;i++) { edges.clear(); s=sont[i]; if(fa[i][0])//把所有连向i上面的边加进去 { ++s; for(int j=head[i];j;j=nxt[j]) { int v=to[j]; if(v!=fa[i][0]&&minn[v]!=inf) edges.push_back((edge){num[v],s,minn[v]}); } } for(int j=0;j<link[i].size();j++)//把i的邻接点之间的连边加进去 edges.push_back((edge){num[link[i][j].u],num[link[i][j].v],link[i][j].d}); sort(edges.begin(),edges.end()); mst=0; for(int j=1;j<=s;j++) pa[j]=j; ns=0; for(int j=0;j<edges.size()&&ns<s-1;j++) { int u=Find(edges[j].u),v=Find(edges[j].v); if(u!=v) { pa[v]=u; ns++; mst+=edges[j].d; } } if(ns<s-1) puts("inf"); else printf("%d\n",smst-w[i]+mst); } } return 0; }
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
· ThreeJs-16智慧城市项目(重磅以及未来发展ai)
· .NET 原生驾驭 AI 新基建实战系列(一):向量数据库的应用与畅想
· Ai满嘴顺口溜,想考研?浪费我几个小时
· Browser-use 详细介绍&使用文档
· 软件产品开发中常见的10个问题及处理方法