题目描述
小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);
}