bzoj2395
每条边有两个权值,c[i],t[i],求一棵生成树使sigma(c[i])*sigma(t[i])最小
ž设Sigma(a)*sigma(b)=k
ž则sigma(b)=k/sigma(a),是个反比例函数,求出y坐标最小的和x坐标最小的,则将两个点连线,之上的点肯定不优,接着用此线去切(以此斜率类似最优比率生成树去判定,此处最好不用实数,很慢很慢),切点是最优解的候选解,再递归去连线(三角形内部点肯定不优)
ž直到新的点在两点直线上。
最后两个点卡常数啊,我调了很久,double不能用,要改为B*C[I]+A*T[I]。原理大约是用凸壳,但是为什么求出斜率后再求mst就不知道了。
次小乘积生成树
žWc众神犇也没yy出来。
ž大概有两种思路。
ž第一种在次小凸壳(每次选次小点)找最小点并在最小凸壳找次小点(正确性完全木有证明)
ž第二种不记得了。
#include <cstdio>
#include <cstdlib>
#include <cstring>
struct line
{
int i;int w;
}a[10050],p,q;
const int maxn=210;
int fa[maxn],n,m,l[10050],r[10050];
int c[10050],t[10050],anc,ant,ans;
double pd;
int find(int x) {if (fa[x]!=x) fa[x]=find(fa[x]);return fa[x];}
void updata(int cc,int tt) {if (cc*tt<ans) ans=cc*tt,anc=cc,ant=tt;}
int cmp1(const void *i,const void *j)
{
p=*(line *)i,q=*(line *)j;
pd=p.w-q.w;
if (pd>0) return 1;else return -1;
}
void kruskal(int &cc,int &tt)
{
int i,j,ll,rr;
qsort(a+1,m,sizeof(a[1]),cmp1);
for (i=1;i<=n;i++) fa[i]=i;
for (i=1;i<=m;i++)
{
ll=find(fa[l[a[i].i]]),rr=find(fa[r[a[i].i]]);
if (ll!=rr) fa[rr]=ll,cc+=c[a[i].i],tt+=t[a[i].i];
}
}
int cmp(int px,int py,int qx,int qy) {return (px*qy-py*qx);}
void dfs(int ac,int at,int bc,int bt)
{
int cc=0,tt=0;
int A=bc-ac,B=at-bt;
for (int i=1;i<=m;i++) a[i].w=B*c[a[i].i]+A*t[a[i].i];
kruskal(cc,tt);
if (cmp(ac-bc,at-bt,cc-bc,tt-bt)<=0) {updata(ac,at),updata(bc,bt);return ;}
dfs(ac,at,cc,tt),dfs(cc,tt,bc,bt);
}
void init()
{
int i,x,y;
int ac=0,at=0,bc=0,bt=0;
scanf("%d%d\n",&n,&m);
for (i=1;i<=m;i++) scanf("%d%d%d%d\n",&x,&y,&c[i],&t[i]),x++,y++,l[i]=x,r[i]=y;
ans=1073741819,anc=0,ant=0;
for (i=1;i<=m;i++) a[i].w=c[i],a[i].i=i;
kruskal(ac,at);updata(ac,at);
for (i=1;i<=m;i++) a[i].w=t[a[i].i];
kruskal(bc,bt);updata(bc,bt);
if ((ac!=bc)||(at!=bt)) dfs(ac,at,bc,bt);
printf("%d %d\n",anc,ant);
}
int main()
{
freopen("timeismoney.in","r",stdin);
freopen("timeismoney.out","w",stdout);
init();
return 0;
}