题意
给出一个(n)个点(m)条边的无向连通图,问删掉每一个点后的最小生成树权值和为多少(如果不存在最下生成树就输出inf)。
(nle 2 imes 10^4,mle 10^5)
分析
换了个超级爽的青轴键盘后写题就开始加速了啊!这样非常不好!!!这道题在写的时候出了很多问题,以后还是要慢慢想清楚再写。
首先一个图的生成树(特别是最小生成树)这种问题,我们可以先把整体的最小生成树建出来。下面就是看删掉一个点会导致什么结果。
把生成树看成一颗有根树,那么删掉一个点会让它的子树,以及父亲节点互相分离。我们要把它用最小的代价连回去。这里的连边只有两种,子树之间的“横向边”和子树连到外面的“纵向边”。
子树内的边的处理基于一条((x,y))边为“子树之间”的边,只有在它们的lca处才会出现一次这样的情况(如果在lca的上面那么它们就属于同一颗子树了)。这样我们就可以把这些边加到数组里。
注意到子树连到外面的多条边中,只有最小的那条是有用的,我们只要想办法求出一个子树连到外面的所有边中最小的就可以了。这可以用线段树合并(dfn序),可并堆(当一条边在一个子树中就pop),树链剖分(考虑在哪些点一条边为“纵向边”,显然是两点之间的路径上除了lca之外的所有点),这几种方法来实现。
这样我们就保证了总的求最小生成树的边数为(O(n+m)),所以可以在(O(n+mlog (n+m)))的时间内解决这个问题。
这里的子树之间的情况分析在别的题中也有广泛的应用。
代码
#include<cstdio>
#include<cctype>
#include<cstring>
#include<vector>
#include<utility>
#include<algorithm>
#define M(x) memset(x,0,sizeof x)
using namespace std;
int read() {
int x=0,f=1;
char c=getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
for (;isdigit(c);c=getchar()) x=x*10+c-'0';
return x*f;
}
const int maxn=2e4+1;
const int maxm=1e5+1;
const int nlogn=3e6;
const int maxj=15;
const int inf=1e9+7;
typedef pair<int,int> Pair;
Pair operator + (Pair a,Pair b) {
return min(a,b);
}
struct bian {
int x,y,w;
bool alr;
bian (int x=0,int y=0,int w=0):alr(false),x(x),y(y),w(w) {}
inline bool operator < (const bian &a) const {return w<a.w;}
} b[maxm];
int with[maxn],ans[maxn],n,m,base,root[maxn];
namespace uns {
int f[maxn];
void clear(int n) {for (int i=1;i<=n;++i) f[i]=i;}
int find(int x) {return f[x]==x?x:f[x]=find(f[x]);}
}
namespace sgt {
struct node {
int l,r;
Pair dat;
} t[nlogn];
int tot;
void update(int x) {
if (t[x].l) t[x].dat=t[x].dat+t[t[x].l].dat;
if (t[x].r) t[x].dat=t[x].dat+t[t[x].r].dat;
}
int merge(int x,int y,int l,int r) {
if (!x) return y;
if (!y) return x;
if (l==r) {
t[x].dat=t[x].dat+t[y].dat;
return x;
}
int mid=(l+r)>>1;
t[x].l=merge(t[x].l,t[y].l,l,mid);
t[x].r=merge(t[x].r,t[y].r,mid+1,r);
update(x);
return x;
}
Pair query(int x,int L,int R,int l,int r) {
if (!x) return make_pair(inf,0);
if (L==l && R==r) return t[x].dat;
int mid=(L+R)>>1;
if (r<=mid) return query(t[x].l,L,mid,l,r);
if (l>mid) return query(t[x].r,mid+1,R,l,r);
return query(t[x].l,L,mid,l,mid)+query(t[x].r,mid+1,R,mid+1,r);
}
Pair query(int x,int l,int r) {
if (l>r) return make_pair(inf,0);
return query(x,1,n,l,r);
}
void modify(int &x,int l,int r,int p,Pair d) {
if (!x) t[x=++tot]=(node){0,0,make_pair(inf,0)};
if (l==r) {
t[x].dat=t[x].dat+d;
return;
}
int mid=(l+r)>>1;
p<=mid?modify(t[x].l,l,mid,p,d):modify(t[x].r,mid+1,r,p,d);
update(x);
}
void modify(int &x,int p,Pair d) {
modify(x,1,n,p,d);
}
}
namespace tree {
vector<int> g[maxn];
vector<bian> on[maxn];
int first[maxn],second[maxn],dfx,f[maxn][maxj],dep[maxn];
int id[maxn];
int getid(int x) {return id[x]==x?x:id[x]=getid(id[x]);}
void clear(int n) {
M(first),M(second),M(f),M(dep),dfx=0;
for (int i=1;i<=n;++i) id[i]=i;
for (int i=1;i<=n;++i) g[i].clear(),on[i].clear();
}
void add(int x,int y) {g[x].push_back(y);}
void dfs(int x,int fa) {
f[x][0]=fa;
dep[x]=dep[fa]+1;
first[x]=++dfx;
for (int v:g[x]) if (v!=fa) dfs(v,x);
second[x]=dfx;
}
void run() {
for (int j=1;j<maxj;++j) for (int i=1;i<=n;++i) f[i][j]=f[f[i][j-1]][j-1];
}
int lca(int x,int y) {
if (dep[x]<dep[y]) swap(x,y);
for (int j=maxj-1;j>=0;--j) if (dep[f[x][j]]>=dep[y]) x=f[x][j];
if (x==y) return x;
for (int j=maxj-1;j>=0;--j) if (f[x][j]!=f[y][j]) x=f[x][j],y=f[y][j];
return f[x][0];
}
void work(int x,int fa) {
vector<bian> &e=on[x];
int gs=(x!=1);
for (int v:g[x]) if (v!=fa) {
work(v,x);
++gs;
uns::f[v]=v;
if (x==1) continue;
Pair ret1=sgt::query(root[v],1,first[x]-1);
Pair ret2=sgt::query(root[v],second[x]+1,n);
Pair ret=ret1+ret2;
if (ret.first<inf) e.push_back((bian){x,v,ret.first});
root[x]=sgt::merge(root[x],root[v],1,n);
}
uns::f[x]=x;
sort(e.begin(),e.end());
int &tmp=ans[x]=base-with[x],j=0;
for (int i=0;i<e.size() && j<gs-1;++i) {
int u=getid(e[i].x),v=getid(e[i].y),w=e[i].w;
int fx=uns::find(u),fy=uns::find(v);
if (fx!=fy) {
uns::f[fx]=fy;
++j;
tmp+=w;
}
}
if (j<gs-1) tmp=-1;
for (int v:g[x]) if (v!=fa) id[v]=x;
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
#endif
int T=read();
while (T--) {
n=read(),m=read();
base=0;
M(root),M(with);
tree::clear(n);
uns::clear(n);
for (int i=1;i<=m;++i) {
int x=read(),y=read(),d=read(),c=read(),w=d*(1-c);
b[i]=(bian){x,y,w};
}
sort(b+1,b+m+1);
for (int i=1,j=0;j<n-1 && i<=m;++i) {
int x=b[i].x,y=b[i].y,w=b[i].w;
int fx=uns::find(x),fy=uns::find(y);
if (fx!=fy) {
++j;
tree::add(x,y),tree::add(y,x);
uns::f[fx]=fy;
with[x]+=w,with[y]+=w;
b[i].alr=true;
base+=w;
}
}
tree::dfs(1,1);
tree::run();
sgt::tot=0;
for (int i=1;i<=m;++i) if (!b[i].alr) {
int x=b[i].x,y=b[i].y,w=b[i].w;
if (tree::dep[x]>tree::dep[y]) swap(x,y);
int l=tree::lca(x,y);
if (x!=l) tree::on[l].push_back(b[i]);
sgt::modify(root[y],tree::first[x],make_pair(w,y));
sgt::modify(root[x],tree::first[y],make_pair(w,x));
}
tree::work(1,1);
for (int i=1;i<=n;++i) ans[i]==-1?puts("inf"):printf("%d
",ans[i]);
}
return 0;
}