bzoj4306: 玩具厂

来源:互联网 发布:淘宝查排名工具 编辑:程序博客网 时间:2024/05/01 00:26

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4306

思路:首先我们可以发现,只有一个点的w是不确定的

那么我们记录一个cost[i],表示在i处建厂除了n点之外的所有点的运输费用之和。

设w[n]=x,tot为环总长,dist[i]表示Σd[j] (1<=j<=i)

于是每个点建厂的真实费用就是形如kx+cost[i]的形式,k根据题意就是n到i的两条路径长度的乘积

真实的费用cst[i]=(tot-dist[i])*dist[i]*x+cost[i]

暴力求cost是O(n^2)的,TLE不解释


而从cost[i-1]推出cost[i]却是可行的

我们要维护一些东西

设l[j],r[j]为j点到i的两条路径长(反正是个环,l,r换个位置也没关系)

cost[i-1]=Σw[j]*l[j]*r[j]

cost[i]=Σw[j]*(l[j]+d[i])*(r[j]-d[i])

做差可得:cost[i]-cost[i-1]=Σw[j]*r[j]*d[i]-Σw[j]*l[j]*d[i]-Σw[j]*d[i]*d[i]

那么我们先暴力求出1号点的cost[1],只要维护好Σw[j]*r[j],Σw[j]*l[j]和Σw[j]即可O(1)转移(然而细节很多...)

Σw[j]好办,关键是Σw[j]*r[j],Σw[j]*l[j],维护好一个,另一个类似

Σw[j]*r[j]为例

从i-1到i后,所有点到i的距离都减少(或增加)了,d[i]除了当前点,特殊处理即可。

于是我们就O(n)求出了cost


现在考虑真实的费用cst[i]=(tot-dist[i])*dist[i]*x+cost[i]

当x在一段区间内时,在一个点建厂最优,不就是它对应的直线在所有直线下方吗

先按斜率排序,我们就可以类似斜率优化,得到哪些直线可能是最优的。

于是我们维护一个栈

设l1=stack[top-1],l2=sta[top],l3为要加进来的直线

画图可知那么当l1和l2交点横坐标大于l2和l3交点横坐标,那l2就不可能是最优的,把它弹出去。

最后栈中的直线就是可能是最优的

因为概率是均匀分布的

那么每个点对应直线所占最优区间长度/x的取值范围长度就是它设厂的概率


注意:有重复的直线,他们要平分概率

#include<cmath>#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>const double eps=1e-9;const int maxn=100010;typedef long long ll;using namespace std;struct line{double k,b;int id;}li[maxn],sta[maxn];int n,same[maxn],to[maxn],top;double sw,rw,lw,A,B,w[maxn],d[maxn],cost[maxn],dist[maxn],tmp,tmp2,tot,p[maxn],res[maxn];//tot 环的总长  //dist[i] 从1到n方向的dis前缀和//p每条直线的概率,因为有重复的,所以还不是答案,ans是答案 double inter(line a,line b){return (b.b-a.b)/(a.k-b.k);}bool zero(double x){return fabs(x)<eps;}bool cmp(line a,line b){if (zero(a.k-b.k)) return a.b<b.b;else return a.k<b.k;}void init(){scanf("%d%lf%lf",&n,&A,&B);for (int i=1;i<n;i++) scanf("%lf",&w[i]);for (int i=1;i<=n;i++){scanf("%lf",&d[i]),tot+=d[i];dist[i]=tot,li[i].id=i;}for (int i=1;i<=n;i++) li[i].k=(dist[i])*(tot-dist[i]);rw=w[1]*tot,sw=w[1];for (int i=2;i<=n;i++){tmp+=w[i]*(dist[i]-dist[1])*(tot-dist[i]+dist[1]);rw+=w[i]*(dist[i]-dist[1]);lw+=w[i]*(tot-dist[i]+dist[1]);sw+=w[i];}li[1].b=tmp;for (int i=2;i<=n;i++){tmp+=rw*d[i]-lw*d[i]-sw*d[i]*d[i];li[i].b=tmp;rw+=w[i]*tot,lw-=w[i]*tot;rw-=sw*d[i],lw+=sw*d[i];}//for (int i=1;i<=n;i++) printf("%.5lf %.5lf\n",li[i].k,li[i].b);}void work(){sort(li+1,li+1+n,cmp);for (int i=1;i<=n;i++){if (i==1||!zero(li[i].k-li[i-1].k)){while (top>1&&inter(li[i],sta[top])>inter(sta[top],sta[top-1])) top--;sta[++top]=li[i];same[top]=1,to[i]=top;}else{if (zero(li[i].b-sta[top].b)) same[top]++,to[i]=top; }}double len=B-A;double le=-(1e100),ri;for (int i=top;i;i--){if (i==1) ri=1e100;else ri=inter(sta[i],sta[i-1]);//printf("%.3lf\n",ri);if (zero(len)){if (le<=A&&ri>=B) p[i]=1.0;}else p[i]=max(0.0,min(ri,B)-max(A,le))/len;//确定每条直线的最优区间及长度占比 le=ri;}for (int i=1;i<=n;i++)if (zero(sta[to[i]].b-li[i].b)&&zero(sta[to[i]].k-li[i].k))res[li[i].id]=p[to[i]]/(1.0*same[to[i]]);for (int i=1;i<=n;i++) printf("%.3lf\n",res[i]);}int main(){//freopen("t1.in","r",stdin);freopen("t1.out","w",stdout);init(),work();return 0;}



0 0
原创粉丝点击