给定一个序列,求一个满足题目三个式子的费用最小的序列的费用。
单调栈,因为满足条件的序列是单增的,若出现下降,则将栈顶元素取出,和现有元素融合,然后在与栈顶元素判断。
#include <cstdio>
#include <cstring>
inline double should(int a,int b) {
if (a+b==0) return 1;
//printf("%d %d\n",a,b);
return (double)a/(a+b);
}
struct Node {
int a,b;
double v;
Node () {}
Node (int aa,int bb) {
a=aa;b=bb;
v=should(a,b);
}
friend Node operator + (const Node &a,const Node &b) {
return Node(a.a+b.a,a.b+b.b);
}
friend bool operator < (const Node &a,const Node &b) {
return a.v<b.v;
}
};
int a[100000];
int num1[100001];
int num0[100001];
Node que[100001];
int n,l,r,q;
int main() {
int t,i,k;
double ans;
scanf("%d",&t);
while (t--) {
scanf("%d",&n);
for (i=0;i<n;i++) {
char c=getchar();
while (c!='1'&&c!='0') c=getchar();
a[i]=c-'0';
}
for (i=0;i<n&&a[i]==0;i++);
l=i;
for (i=n-1;i>=0&&a[i]==1;i--);
r=i;
//printf("%d %d\n",l,r);
if (l>r) ans=0;
else {
num1[r+1]=0;
num0[r+1]=0;
for (i=r;i>=l;i--) {
if (a[i]==0) {
num1[i]=num1[i+1];
num0[i]=num0[i+1]+1;
} else {
num1[i]=num1[i+1]+1;
num0[i]=num0[i+1];
}
}
i=l;
ans=0;
q=0;
while (i<=r) {
k=i;
while (a[i]==1&&i<=r) i++;
while (a[i]==0&&i<=r) i++;
Node x=Node(num1[k]-num1[i],num0[k]-num0[i]);
while (q&&x<que[q-1]) {
q--;
x=que[q]+x;
}
que[q++]=x;
//printf("---\n");
//for (k=0;k<q;k++) printf("%d %d %lf\n",que[k].a,que[k].b,que[k].v);
}
for (i=0;i<q;i++) {
ans+=que[i].a*(1-que[i].v)*(1-que[i].v)+que[i].b*que[i].v*que[i].v;
}
}
printf("%.6lf\n",ans);
}
return 0;
}