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;
}

 

转载于:https://www.cnblogs.com/Zinn/p/10131771.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值