WC 2010 efield 能量场

首先吐槽WC2010:完全毁了“WC不可做”的神奇形象。。。。。。

首先是本弱菜被误导了,以为是个动态规划。。。。。。

废话少说,首先mamb(ca-cb)化成maca*mb-mbcb*ma。。。这告诉我们关于两者相关的式子,要往把单体信息化到一起,可能会发现很神奇的东西。。。

令xa=maca ya=ma,上式就成了叉积。。。

所以原题就成了给定在第一象限的n个点,求两两最大叉积的方案,和最大简单多边型面积。。。之所以是简单多边形,因为要求负值要连续,又要最大,那么就是凸包了。。。

那么讲一下两两叉积最大吧。。。首先方案一定时凸包上两点。。。因为凸包上两点才可以达到点集最大距离。。。而叉积最大又是可以等价于方案两点与原点形成的平行四边形面积最大。。。一种方法:将原点“加入"凸包,固定原点为三角形上的一点卡壳法求最大三角形。。。

然后注意方案输出顺序。。。

Code:

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#define zero(x) ((fabs(x)<1e-8))
struct point
{
  double x,y;
} qup[50005],qdn[50005],que[50005],node[50005];
int indexup[50005],indexdn[50005],index[50005],n=0,hea=0,tai=0,top=0,ansi=0,ansj=0,oriindex[50005];
double ans2=0;
int cmp(const void *p,const void *q)
{
  int a=*(int*)p,b=*(int*)q;
  if (zero(node[a].x-node[b].x))
    if (zero(node[a].y-node[b].y))
      return 0;
    else
      if (node[a].y-node[b].y>0)
	return 1;
      else
	return -1;
  else
    if (node[a].x-node[b].x>0)
      return 1;
    else
      return -1;
}
double cross(point a,point b,point c,point d)
{
  b.x-=a.x;b.y-=a.y;
  d.x-=c.x;d.y-=c.y;
  return b.x*d.y-d.x*b.y;
}
double cross(point a,point b)
{
  return a.x*b.y-b.x*a.y;
}
int main()
{
  freopen("efield.in","r",stdin);
  freopen("efield.out","w",stdout);
  scanf("%d",&n);
  int i=0,j=0;
  for (i=1;i<=n;i++)
    {
      scanf("%lf%lf",&node[i].y,&node[i].x);
      node[i].x=node[i].x*node[i].y;
      oriindex[i]=i;
    }
  qsort(oriindex+1,n,sizeof(oriindex[0]),cmp);
  qup[++hea]=node[oriindex[1]];
  qup[++hea]=node[oriindex[2]];
  qdn[++top]=node[oriindex[1]];
  qdn[++top]=node[oriindex[2]];
  indexup[1]=indexdn[1]=oriindex[1];
  indexup[2]=indexdn[2]=oriindex[2];
  for (i=3;i<=n;i++)
    {
      while (cross(qup[hea-1],qup[hea],qup[hea],node[oriindex[i]])>=0 && hea>1) hea--;
      while (cross(qdn[top-1],qdn[top],qdn[top],node[oriindex[i]])<=0 && top>1) top--;
      qup[++hea]=node[oriindex[i]];
      indexup[hea]=oriindex[i];
      qdn[++top]=node[oriindex[i]];
      indexdn[top]=oriindex[i];
    }
  for (i=1;i<=hea;i++)
    que[++tai]=qup[i],index[tai]=indexup[i];
  for (i=top-1;i>1;i--)
    que[++tai]=qdn[i],index[tai]=indexdn[i];
  j=1;
  for (i=1;i<=tai;i++)
    {
      while (fabs(cross(que[i],que[j==tai?1:j+1]))>fabs(cross(que[i],que[j]))) j=j==tai?1:j+1;
      if (fabs(cross(que[i],que[j]))>ans2)
	{
	  ansi=i;
	  ansj=j;
	  ans2=fabs(cross(que[i],que[j]));
	}
    }
  if (cross(que[ansi],que[ansj])>=0)
    printf("%d %d\n",index[ansi],index[ansj]);
  else
    printf("%d %d\n",index[ansj],index[ansi]);
  printf("%d\n",tai);
  for (i=tai;i>=1;i--) printf("%d ",index[i]);
  return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值