Description
给你一个n个点和m条边,每条边有两种权值:黑权值和白权值,
你需要构建一棵连接这n个点的生成树,使得这n-1条边,有不超过k条边用白权值,其他的用黑权值。
对于每个
k
∈
[
0
,
n
−
1
]
k\in [0,n-1]
k∈[0,n−1]你都需要求出答案。
n
≤
1000
,
m
≤
200000
,
边
权
≤
1000
n\leq 1000, m\leq 200000, 边权\leq 1000
n≤1000,m≤200000,边权≤1000
Solution
题意相当于:有2m条边,你需要构建一棵生成树,使得选的边中有不超过k条白边,剩下的选黑边,
我们从小到大枚举k,每次选若干条白边,顶掉若干条黑边。
显然的,白边一旦被选上就不会再被顶掉了。所以我们对所有的白边做一次生成树,被选上的白边才是有用的。
我们并不好直接控制选多少条白边,考虑给每个白边全部加上一个权值c,当c很大时我们选择的白边就会很少,反之亦然。
我们发现这本质上就是每次选择一条白边(x,y),把原生成树上x到y路径上权值最大的边给顶掉,
这个c其实就是顶掉以后,生成树减少的权值。
从大到小枚举c,顶掉的黑边就会越来越多,换句话说:我们是在贪心的顶掉黑边,每次选能最大程度减少权值的白边。
如果出现c-=1以后一下子选了一大堆白边,那说明这一堆白边对权值的减少量都是相同的,选哪一条答案都一样。
所以我们做的时候,就每次看看能不能多选一条白边,如果可能就计算答案,不可以就c-=1,直到可以。
复杂度: O ( n 2 ) O(n^2) O(n2)
Code
#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define efo(i,q) for(int i=0;i<(int)B[q].size();++i)
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
const int N=1500,M=200500,INF=1e9+7;
#define S 1000000
char bf[S], *p1 = bf, *p2 = bf;
#define nc() (p1==p2&&(p2=(p1=bf)+fread(bf,1,S,stdin),p2==p1)?-1:*p1++)
inline int read(int &x) {
x = 0;
char ch = nc();
for (; ch < '0' || ch > '9'; ch = nc());
for (; ch <= '9' && ch >= '0'; x = x * 10 + ch - 48, ch = nc());
return x;
}
unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
mt19937 rand_num(seed); // 大随机数
int RD(int q){return rand_num()%q;}
int n,m,ans;
int g[N];
int gf(int q){return g[q]==q?q:g[q]=gf(g[q]);}
struct Val
{
int x,y,v;
}sc[M],sc1[M];
bool z[M];
int Ans[N];
int doit(int e,int AD)
{
int cnt0=0,cnt1=0,q,w;
int ans=0;
fo(i,1,n)g[i]=i;
for(int i=1,j=1;cnt0+cnt1<n-1;)
{
if(sc[i].v>sc1[j].v+AD)
{
q=sc1[j].x,w=sc1[j].y;
if(gf(q)!=gf(w)&& (cnt1<e||z[j]))
{
ans+=sc1[j].v;
g[gf(q)]=g[w];
if(!z[j])
{
z[j]=1;
++cnt1;
}else ++cnt0;
}
++j;
}else{
q=sc[i].x,w=sc[i].y;
if(gf(q)!=gf(w))
{
ans+=sc[i].v;
g[gf(q)]=g[w];
++cnt0;
}
++i;
}
}
return cnt1==e?ans:INF;
}
void init()
{
int q,w,e;
read(n),read(m);
ans=0;
fo(i,1,m)
{
read(q),read(w),read(e);
sc[i]={q,w,e};
read(e);
sc1[i]={q,w,e};
z[i]=0;
}
sort(sc+1,sc+1+m,[](Val q,Val w){return q.v<w.v;});
sort(sc1+1,sc1+1+m,[](Val q,Val w){return q.v<w.v;});
fo(i,1,n)g[i]=i;
int cnt=0;
fo(i,1,m)
{
q=sc[i].x,w=sc[i].y;
if(gf(q)!=gf(w))
{
ans+=sc[i].v;
g[gf(q)]=g[w];
++cnt;
swap(sc[i],sc[cnt]);
}
}
sc[n].v=INF;
fo(i,1,n)g[i]=i;
cnt=0;
fo(i,1,m)
{
q=sc1[i].x,w=sc1[i].y;
if(gf(q)!=gf(w))
{
g[gf(q)]=g[w];
++cnt;
swap(sc1[i],sc1[cnt]);
}
}
sc1[n].v=INF;
Ans[0]=ans;
int AD=1000;
fo(i,1,n-1)
{
ans=INF;
for(;AD>=0&&(ans=doit(1,AD))==INF;--AD);
Ans[i]=min(Ans[i-1],ans);
}
fo(i,0,n-1)printf("%d\n",Ans[i]);
}
int main()
{
int q,w,_;
read(_);
while(_--)
{
init();
}
return 0;
}