洛谷5155 [USACO18DEC]Balance Beam (凸包+推柿子)

usaco铂金组的题
qwq
感觉有些题解其实说的并不是很明白啊。

首先,这个题目其实并不好入手的啊。

首先,这里先给定一个性质,就是说,在一个长度为 L L L的数轴上,当前在 x x x的位置,按照题目给的方式去走的话,走到 L L L这一端的概率是 x L \frac{x}{L} Lx,另一端的概率是 L − x L \frac{L-x}{L} LLx

证明:
不难发现,假设我们设 f ( x ) f(x) f(x)表示 x x x这个点到 L L L的概率,那么 f ( L ) = 1 , f ( 0 ) = 0 f(L) = 1,f(0)=0 f(L)=1,f(0)=0

由于 f ( x ) = f ( x − 1 ) + f ( x + 1 ) 2 f(x)=\frac{f(x-1)+f(x+1)}{2} f(x)=2f(x1)+f(x+1)的形式

所以,不难发现 f ( x ) f(x) f(x)是一个等差数列的形式

那么就能推出来上面这个性质了。

那么对于一个点来说,要么原地不动,直接停止,要么就是向两边走。
假设我们靠左走,到 a a a处停止,右边则是到 b b b处停止

那么我们的期望收益就是 e = m a x ( ( i − a ) v a l b b − a + ( b − i ) v a l a b − a , v a l i ) e =max( \frac{(i-a)val_b}{b-a} + \frac{(b-i)val_a}{b-a} , val_i ) e=max(ba(ia)valb+ba(bi)vala,vali)

qwq经过一些脑洞,我们惊奇的发现。

如果将对于每一个点看成一个二维平面的点 ( x , v a l x ) (x,val_x) (x,valx),那么上述柿子,就是 i i i a , b a,b a,b形成的直线上的对应的函数值。

因为上式等于 v a l b − v a l a b − a + v a l a ( b − a ) b − a \frac{val_b-val_a}{b-a} + \frac{val_a(b-a)}{b-a} bavalbvala+bavala(ba)

稍微合并一下就能得到上面这个柿子了。

那么我们发现,对于一个非停止点,他的权值是小于对应的函数值的,但是停止点,则是大于两边的函数值,这时候,我们发现

这就是一个凸包啊!

我们只需要统计出每个点两边最近的一个凸包上的点,然后更新一下就 o k ok ok了,貌似确实是这样的,那么这个题就这么做完了

但是有一个需要注意的地方就是说,我们需要把 ( 0 , 0 ) 和 ( n + 1 , 0 ) (0,0)和(n+1,0) (0,0)(n+1,0)也加入凸包,因为两端是给出的停止点。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk make_pair
#define int long long
using namespace std;
inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}
const int maxn = 3e5+1e2;
const int ptx = 1e5;
struct Point
{
	int x,y;
};
int val[maxn],l[maxn],r[maxn];
int n,m;
Point st[maxn];
int top;
Point a[maxn];
int tag[maxn];
Point b[maxn];
int tmp;
int ans[maxn];
Point operator + (Point a,Point b)
{
	return (Point){a.x+b.x,a.y+b.y};
}
Point operator - (Point a,Point b)
{
	return (Point){a.x-b.x,a.y-b.y};
}
int chacheng(Point a,Point b)
{
	return a.x*b.y-a.y*b.x;
}
int dis(Point a,Point b)
{
	Point now = a-b;
	return now.x*now.x+now.y*now.y;
}
Point ymh;
bool cmp(Point a,Point b)
{
	int now = chacheng(a-ymh,b-ymh);
	if (now>0) return 1;
	if (now<0) return 0;
	else
	{
		int d1 = dis(a,ymh);
		int d2 = dis(b,ymh);
		return d1<d2;
	} 
}
void solve(int n)
{
	int mn = 1e9,pos=0;
	for (int i=1;i<=n;i++)
	{
		if (a[i].y<mn)
		  mn=a[i].y,pos=i;
		else
		{
			if (a[i].y==mn && a[i].x<a[pos].x)
			  mn=a[i].y,pos=i;
		}
	}
	swap(a[1],a[pos]);
	ymh = a[1];
	sort(a+1,a+1+n,cmp);
	top=1;
	st[1]=a[1];
	a[n+1]=a[1];
	for (int i=2;i<=n+1;i++)
	{
		while(top>1 && chacheng(st[top]-st[top-1],a[i]-st[top-1])<=0) top--;
		st[++top]=a[i];
	} 
	for (int i=1;i<top;i++) b[++tmp] = st[i],tag[st[i].x]=1;
}
signed main()
{
  n=read();
  for (int i=1;i<=n;i++) val[i]=read(),a[i].x=i,a[i].y=val[i];
  int nn = n;
  ++nn;
  a[nn].x=0,a[nn].y=0;
  ++nn;
  a[nn].x=n+1,a[nn].y=0;
  solve(nn);
  l[0]=0;
  for (int i=1;i<=n;i++) 
  {
  	if(tag[i]) l[i]=i;
  	else l[i]=l[i-1];
  }
  r[n+1]=n+1;
  for (int i=n;i>=1;i--)
  {
  	 if (tag[i]) r[i]=i;
  	 else r[i]=r[i+1];
  }
  for (int i=1;i<=n;i++)
  {
  	//cout<<r[i]<<" "<<l[i]<<endl;
  	 if(l[i]==r[i]) ans[i]=val[i]*ptx; //这个点是凸包上的点 
  	 else
  	 {	
  	   //这一步是求这个点在两侧凸包上的点的 连线上的函数值。 
  	   ans[i]=((val[r[i]]-val[l[i]])*(i-l[i])+val[l[i]]*(r[i]-l[i]))*ptx/(r[i]-l[i]);	
	 }
  }
  for (int i=1;i<=n;i++)
  {
  	  cout<<ans[i]<<"\n";
  }
  return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值