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; }