bzoj 2395 Timeismoney —— 最小乘积生成树
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2395
参考博客:https://www.cnblogs.com/autsky-jadek/p/3959446.html
但复杂度不太会算;
递归边界不要取两个点相等,而是叉积>=0 。
代码如下:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; int const xn=205,xm=1e4+5; int n,m,fa[xn],dis[xn]; ll ansx,ansy; struct N{ int u,v,a,b; ll w; bool operator < (const N &y) const {return w<y.w;} }ed[xm]; struct P{ ll x,y; P(ll x=0,ll y=0):x(x),y(y) {} }; P operator - (const P &A,const P &B){return P(A.x-B.x,A.y-B.y);} ll cross(P A,P B){return A.x*B.y-A.y*B.x;} int rd() { int ret=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return f?ret:-ret; } int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);} void uni(int x,int y) { if(dis[x]==dis[y])fa[x]=y,dis[y]++; else if(dis[x]<dis[y])fa[x]=y; else fa[y]=x; } P kruskal() { int cnt=0; P ret=P(0,0); memset(dis,0,sizeof dis); for(int i=1;i<=n;i++)fa[i]=i; sort(ed+1,ed+m+1); for(int i=1;i<=m;i++) { int x=find(ed[i].u),y=find(ed[i].v); if(x==y)continue; ret.x+=ed[i].a; ret.y+=ed[i].b; uni(x,y); cnt++; if(cnt==n-1)break; } return ret; } bool eql(P A,P B){return A.x==B.x&&A.y==B.y;} void solve(P A,P B) { for(int i=1;i<=m;i++)ed[i].w=ed[i].b*(B.x-A.x)+ed[i].a*(A.y-B.y); P C=kruskal(); if(cross(B-A,C-A)>=0)return;//>=0 //if(eql(A,C)||eql(B,C))return; if((C.x*C.y<ansx*ansy)||(C.x*C.y==ansx*ansy&&C.x<ansx))ansx=C.x,ansy=C.y; solve(A,C); solve(C,B); } int main() { n=rd(); m=rd(); for(int i=1;i<=m;i++) ed[i].u=rd()+1,ed[i].v=rd()+1,ed[i].a=rd(),ed[i].b=rd(); for(int i=1;i<=m;i++)ed[i].w=ed[i].a; P A=kruskal(); for(int i=1;i<=m;i++)ed[i].w=ed[i].b; P B=kruskal(); if((A.x*A.y<B.x*B.y)||(A.x*A.y==B.x*B.y&&A.x<B.x))ansx=A.x,ansy=A.y; else ansx=B.x,ansy=B.y; solve(A,B); printf("%lld %lld\n",ansx,ansy); return 0; }