bzoj2395
以前聽基哥講的時候就沒怎麼懂,以爲好難寫好難寫 // 其實不難寫,只是有點難調。
利用數形結合的思想,每棵生成樹在座標系上對應的是點(sigma(a),sigma(b)), 那麼,最小乘積生成樹必定在某個k最小的反比例函數xy= k中。
先求出sigma(a)最小的點,sigma(b)最小的點,利用快包思想,找離這兩點所連成的直線最遠(往靠近原點那邊)的點(生成樹)c,得到一個三角形,三角形
內部的點是不如c優的,可以排除,然後遞歸處理a -->c ,c-->b的情況。
複雜度......如同自適應辛普森一樣,不好確定複雜度,但是速度是可以相信的。
由於腦抽寫了kruscal(圖方便,prim醜了點),結果慢的一塌糊塗= 。 =,加了點小優化才過,不過仍然墊底。
不想改prim了,雖然prim也很好寫。 這一次因禍得福終於學會調快排了= 。 =!,慶祝一下。
# include <cstdlib>
# include <cstdio>
# include <cmath>
using namespace std;
const int maxV= 10000+5;
struct point
{
int a,b,x,y; long long c;
void read()
{
scanf("%d%d%d%d",&x,&y,&a,&b);
x++,y++;
}
}ans,edge[maxV];
int ufs[300];
int n,m;
int find(int x) {return ufs[x]==x? x:x= find(ufs[x]);};
int cmp(const void *i, const void *j)
{
point p=*(point *)i, q=*(point *)j;
if (p.c>q.c) return 1; else return -1;
}
inline point kruscal()
{
int i,fx,fy; point p; p.a=p.b=0; int k = 0;
for (i = 1; i <= n; i++) ufs[i] = i;
for (i = 1; i <= m; i++)
{
fx = find(edge[i].x), fy = find(edge[i].y);
if (fx!= fy) p.a+= edge[i].a, p.b+= edge[i].b, ufs[fx]=fy, k++;
if (k == n-1) break;
}
if (1LL*ans.a*ans.b>1LL*p.a*p.b) ans = p;
return p;
}
inline long long cross(long long x1, long long y1, long long x2, long long y2)
{
return x1*y2-x2*y1;
}
void work(point blim, point alim)
{
int kb = alim.b-blim.b, ka= alim.a-blim.a, i; point p;
for (i = 1; i <= m; i++) edge[i].c= 1LL*edge[i].a*kb-1LL*edge[i].b*ka;
qsort(edge+1, m, sizeof(edge[1]), cmp);
p = kruscal();
if (cross(1LL*alim.a-blim.a, 1LL*alim.b-blim.b, 1LL*p.a-blim.a, 1LL*p.b-blim.b) <=0) return;
work(p, alim);
work(blim, p);
}
int main()
{
int i; point alim, blim;
freopen("timeismoney.in", "r", stdin);
freopen("timeismoney.out", "w", stdout);
scanf("%d%d", &n, &m); ans.a=ans.b=1073740819;
for (i = 1; i <= m; i++)
edge[i].read();
for (i = 1; i <= m; i++) edge[i].c=edge[i].a;
qsort(edge+1, m, sizeof(edge[1]),cmp);
alim = kruscal();
for (i = 1; i <= m; i++) edge[i].c=edge[i].b;
qsort(edge+1, m, sizeof(edge[1]),cmp);
blim = kruscal();
work(blim, alim);
printf("%d %d",ans.a, ans.b);
return 0;
}