前言
考炸了CSP,调整调整心态。。。
然后就学了这个地步。
感觉学较为简单的算法真的能够愉悦身心。
参考文献
个题题解
题集:https://www.cnblogs.com/HocRiser/p/9834069.html
关于整点证明的启发虽然我没看懂:https://www.cnblogs.com/CreeperLKF/p/9045491.html
关于最小度限制生成树与WQS二分的区别:https://www.luogu.com.cn/blog/EndSaH/solution-cf125e
例题演示
例题
中文题意,自己看吧。。。
从题解出发的讲解
如果你没有思路的话,我可以很负责任的告诉你。
刚开始我也没思路,就连题解需要的前提条件都是我已经证明了题解做法正确性之后才推出的前提条件。
我采用的是克鲁斯卡尔的做法。
我们思考一下,我们要让生成树包含一定量的白边,那么就要改变白边在生成树中的位置,那么我们可以给每条边加上一个权值 k k k(可以为正,可以为负)。
当然这里需要证明一个东西,就是增加的数字越小,白边选中的也就越多,也许你会说是废话,但是有时候这些东西还是要证明的,感性理解太吃shit了。
首先,对于前面的一条为未选中的白边, A − > B A->B A−>B,那么在生成树中肯定有 A − > B A->B A−>B的一条链。
我们对于其中权值最大的黑边而言(箭头指的边,如果没有的话,那么这个白边不可能被选中),白边的值原本大于黑边,当加完权值后大于最大的黑边的话,那么这条黑边将彻底淘汰,也就是多了一条白边。
可以发现,这个权值 k k k是整数就够了,不用是小数,如果 k k k加 1 1 1减 1 1 1产生了大量白边怎么办呢?后面会讲。
那么我们现在就是要证 k − 1 k-1 k−1的生成树比 k k k的生成树的白边要多或者相同。
那么也就是对于 k k k的生成树中,更多的白边取代了黑边,而原本的白边,也不会消失,为什么,因为不可能有白边超过他呀。
同时对于 k k k越大而言呢,你可以视作其实是黑边的加上 − k -k −k,然后证明黑边数量递增就行了。
所以就是正确的了,然后跑完以后只要把生成树的代价减去白边的数量就行了。
那么其实我们就可以通过二分 k k k值来控制白边的数量。
但是其实还涉及一个问题,什么问题呢?
就是如果有相同权值的白边和黑边,选什么。
那么我们只要在排序优先白边,然后问大于等于就行了。
不过需要注意一个事情,就是什么呢吗,就是白边虽然选的多了,但是其实我们只是来统计数量的,我们最终只选 n e e d need need个白边,多出的就当选了黑边了,所以每次减去也是 n e e d ∗ k need*k need∗k。
当然,你也可以用这个来证明一个事情,就是选不同数量的白边都可以在对应的 k k k成为最小值。
那么就可以上代码了。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 51000
#define M 110000
using namespace std;
int fa[N];
int findfa(int x)
{
if(fa[x]==x)return x;
return (fa[x]=findfa(fa[x]));
}
struct node
{
int x,y,c,type;
}a[M];int len,dep/*白边的系数*/,need;
inline void ins(int x,int y,int c,int type){
len++;a[len].x=x;a[len].y=y;a[len].c=c;a[len].type=type;}
inline bool cmp(node x,node y){
return (x.c+x.type*dep)==(y.c+y.type*dep)?x.type>y.type:(x.c+x.type*dep)<(y.c+y.type*dep);}
int n,m,ans,zans;
int check()
{
sort(a+1,a+m+1,cmp);
for(int i=1;i<=n;i++)fa[i]=i;
int cnt=0,bai=0;ans=0;
for(int i=1;i<=m;i++)
{
int x=a[i].x,y=a[i].y;
int tx=findfa(x),ty=findfa(y);
if(tx!=ty)
{
fa[tx]=ty;
cnt++;ans+=a[i].c;
if(a[i].type)bai++,ans+=dep;
if(cnt==n-1)return bai;
}
}
}
int main()
{
// freopen("tree7.in","r",stdin);
scanf("%d%d%d",&n,&m,&need);
for(int i=1;i<=m;i++)
{
int x,y,c,type;scanf("%d%d%d%d",