小C饮水记

68 篇文章 0 订阅
21 篇文章 0 订阅

题目描述


小C虽然没有参加NOI2016, 但当他看到"国王饮水记"这题时还是迅速秒掉了.
小C认为这题太水了,于是他决定对这题进行加强.
现在小C桌上有n杯水排成一行,第i杯水中有wi 单位体积的水. 他会选择一个区间[l, r],
并拿一个初始为空的杯子(杯子的容积无限大),他可以重复无限次以下操作:
• 选定任意一杯水i,i ∈ [l, r].
• 使i和它拿着的杯子里的水的体积变为它们的平均值.
小C希望进行若干操作后最大化杯子里的水的体积,设g(l, r)为这个最大值.你需要求:

输入

从文件drink.in中读入数据.
第一行一个整数n.
第二行n个整数,第i个为wi.

输出

输出到文件drink.out中.
输出一个实数表示答案.
你的答案被认为是正确的,当且仅当其与标准答案的绝对误差不超过10^−2 .

这题告诉我们,答案为小数的题目,如果不mod,就要把卡精度视为一种AC策略

贪贪心就好了,卡卡精度就A了,好像国王饮水记是发了一个高精度小数模板吧?

还有用数组维护链表,虽然只能删但功能强大,类比于只能加的并查集


ACcode:

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define maxn 1000005
#define S 23
using namespace std;

int n;
double wt[maxn],Pow2[S+1];
int pre[maxn],suc[maxn],c[maxn];//用数组维护的链表虽然只支持删除,但也支持O(1)下标查询

inline bool cmp(const int &a,const int &b)
{
	return wt[a]<wt[b];
}

char ch;
template <class T>
inline void get(T &res)
{
	while(!isdigit(ch=getchar()));
	for(res=ch-'0';isdigit(ch=getchar());res=res*10+ch-'0');
}

int main()
{
	
	//freopen("drink.in","r",stdin);
	//freopen("drink.out","w",stdout);
	
	
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		get(wt[i]),suc[i]=i+1,pre[i]=i-1,c[i]=i;
	sort(c+1,c+1+n,cmp);
	
	Pow2[0]=1;
	for(int i=1;i<=S;i++) Pow2[i]=Pow2[i-1]*0.5;
		
	int a,b,j,u;
	double ans=0,sum1=0,sum2=0;
	for(int i=1;i<=n;i++)
	{
		u=c[i];
		for(sum1=sum2=0,a=b=u,j=1;j<=S;j++)
		{
			if(a) sum1+=Pow2[j]*(a-pre[a]),a=pre[a];
			if(b<=n) sum2+=Pow2[j]*(suc[b]-b),b=suc[b];
			if(!a && b>n) break;
		}
		ans+=2*wt[c[i]]*sum1*sum2;
		pre[suc[c[i]]]=pre[c[i]];
		suc[pre[c[i]]]=suc[c[i]];
	}
	
	printf("%.12lf",ans/n/n);
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值