题意:给一个图,求删除每个点后的最小生成树权值
设$f(a,b)$表示$a\rightarrow b$路径上第一个不是$a$的点
先求出最小生成树,考虑删掉一个点$x$对最小生成树的影响:它把这棵树分成了$x$的许多子树和剩下的一整部分,我们要用一些非树边把这些连通块连起来
①子树之间的连接:一条非树边$(a,b)$能把$x$的子树连起来当且仅当$\text{lca}_{a,b}=x$,这个直接预处理就好了
②子树和$x$以外的连接:对一条非树边$(a,b)$,当$x$在($fa_a$到$f(\text{lca}_{a,b},a)$路径上或$fa_b$到$f(\text{lca}_{a,b},b)$路径上)时,$(a,b)$是满足要求的边
因为我们要求每个子树都和外界相连,所以我们对$a\rightarrow f\left(f(\text{lca}_{a,b},a),a\right)$和$b\rightarrow f\left(f(\text{lca}_{a,b},b),b\right)$分别用$w_{a,b}$取$\min$,最后查询一下即可
连接子树之间的边有$O(m)$条,子树和$x$以外的连边每次只需找一条,所以做最小生成树的总边数是$O(m+n)$
毒瘤==
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
using namespace std;
const int inf=1000000000;
struct edge{
int x,y,w;
bool f;
edge(int a=0,int b=0,int c=0){x=a;y=b;w=c;f=0;}
}e[100010];
bool operator<(edge a,edge b){return a.w<b.w;}
int sfa[20010];
int get(int x){return x==sfa[x]?x:(sfa[x]=get(sfa[x]));}
void merge(int x,int y){sfa[get(x)]=get(y);}
int kruskal(edge*e,int m){
int i,s,x,y;
sort(e+1,e+m+1);
s=0;
for(i=1;i<=m;i++){
x=get(e[i].x);
y=get(e[i].y);
if(x!=y){
s+=e[i].w;
e[i].f=1;
merge(x,y);
}
}
return s;
}
int h[20010],nex[40010],to[40010],v[40010],M,n;
void add(int a,int b,int c){
M++;
to[M]=b;
v[M]=c;
nex[M]=h[a];
h[a]=M;
}
int fa[20010],siz[20010],son[20010],dep[20010],bl[20010],pos[20010];
void dfs(int x){
int i,k=0,mx=0;
siz[x]=1;
dep[x]=dep[fa[x]]+1;
for(i=h[x];i;i=nex[i]){
if(to[i]!=fa[x]){
fa[to[i]]=x;
dfs(to[i]);
siz[x]+=siz[to[i]];
if(siz[to[i]]>mx){
mx=siz[to[i]];
k=to[i];
}
}
}
son[x]=k;
}
void dfs(int x,int chain){
pos[x]=++M;
bl[x]=chain;
if(son[x])dfs(son[x],chain);
for(int i=h[x];i;i=nex[i]){
if(to[i]!=fa[x]&&to[i]!=son[x])dfs(to[i],to[i]);
}
}
int lca(int x,int y){
while(bl[x]!=bl[y]){
if(dep[bl[x]]<dep[bl[y]])swap(x,y);
x=fa[bl[x]];
}
return dep[x]<dep[y]?x:y;
}
int jump(int x,int y){
while(bl[x]!=bl[y]){
if(dep[bl[x]]<dep[bl[y]])swap(x,y);
if(fa[bl[x]]==y)return bl[x];
x=fa[bl[x]];
}
if(dep[x]>dep[y])swap(x,y);
return son[x];
}
edge mn[80010],co[80010];
void build(int l,int r,int x){
mn[x].w=co[x].w=inf;
if(l==r)return;
int mid=(l+r)>>1;
build(l,mid,x<<1);
build(mid+1,r,x<<1|1);
}
void gao(int x,edge e){
co[x]=min(co[x],e);
mn[x]=min(mn[x],e);
}
void pushdown(int x){
if(co[x].w!=inf){
gao(x<<1,co[x]);
gao(x<<1|1,co[x]);
co[x].w=inf;
}
}
void pushup(int x){mn[x]=min(mn[x<<1],mn[x<<1|1]);}
void modify(int L,int R,edge e,int l,int r,int x){
if(L<=l&&r<=R)return gao(x,e);
pushdown(x);
int mid=(l+r)>>1;
if(L<=mid)modify(L,R,e,l,mid,x<<1);
if(mid<R)modify(L,R,e,mid+1,r,x<<1|1);
pushup(x);
}
void modify(int x,int y,edge e){
while(bl[x]!=bl[y]){
if(dep[bl[x]]<dep[bl[y]])swap(x,y);
modify(pos[bl[x]],pos[x],e,1,n,1);
x=fa[bl[x]];
}
if(dep[x]>dep[y])swap(x,y);
modify(pos[x],pos[y],e,1,n,1);
}
edge query(int p,int l,int r,int x){
if(l==r)return mn[x];
pushdown(x);
int mid=(l+r)>>1;
if(p<=mid)
return query(p,l,mid,x<<1);
else
return query(p,mid+1,r,x<<1|1);
}
vector<edge>cr[20010];
vector<edge>::iterator it;
int ori;
edge tmp[100010];
void gay(int x){
if(x==3){
x=3;
}
int i,s,p;
edge t;
s=ori;
M=0;
for(i=h[x];i;i=nex[i]){
s-=v[i];
sfa[to[i]]=to[i];
if(to[i]!=fa[x]){
t=query(pos[to[i]],1,n,1);
if(t.w!=inf){
tmp[++M]=t;
sfa[t.x]=t.x;
sfa[t.y]=t.y;
}
}
}
for(it=cr[x].begin();it!=cr[x].end();it++){
sfa[it->x]=it->x;
sfa[it->y]=it->y;
}
for(i=1;i<=M;i++){
p=lca(tmp[i].x,x);
if(p!=x){
merge(tmp[i].y,jump(tmp[i].y,x));
merge(tmp[i].x,fa[x]);
}else{
merge(tmp[i].x,jump(tmp[i].x,x));
merge(tmp[i].y,fa[x]);
}
}
for(it=cr[x].begin();it!=cr[x].end();it++){
tmp[++M]=*it;
merge(it->x,jump(it->x,x));
merge(it->y,jump(it->y,x));
}
s+=kruskal(tmp,M);
p=to[h[x]];
for(i=h[x];i;i=nex[i]){
if(get(to[i])!=get(p)){
puts("inf");
return;
}
p=to[i];
}
printf("%d\n",s);
}
void work(){
int m,i,u,v,d,c;
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++){
scanf("%d%d%d%d",&u,&v,&d,&c);
e[i]=edge(u,v,d*(1-c));
}
for(i=1;i<=n;i++){
sfa[i]=i;
cr[i].clear();
}
ori=kruskal(e,m);
memset(h,0,sizeof(h));
M=0;
for(i=1;i<=m;i++){
if(e[i].f){
add(e[i].x,e[i].y,e[i].w);
add(e[i].y,e[i].x,e[i].w);
}
}
dfs(1);
M=0;
dfs(1,1);
build(1,n,1);
for(i=1;i<=m;i++){
if(!e[i].f){
u=e[i].x;
v=e[i].y;
d=lca(u,v);
if(d!=u&&d!=v)cr[d].push_back(e[i]);
if(dep[u]-dep[d]>1)modify(u,jump(u,jump(u,d)),e[i]);
if(dep[v]-dep[d]>1)modify(v,jump(v,jump(v,d)),e[i]);
}
}
for(i=1;i<=n;i++)gay(i);
}
int main(){
int T;
scanf("%d",&T);
while(T--)work();
}