zoukankan      html  css  js  c++  java
  • bzoj4306: 玩具厂

    传送门: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
    ",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
    ",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
    ",res[i]);
    }
    
    int main(){
    	//freopen("t1.in","r",stdin);freopen("t1.out","w",stdout);
    	init(),work();
    	return 0;
    }



  • 相关阅读:
    mysql-5-aggregation
    mysql-4-functions
    mysql-3-orderby
    技术之心 | 云信和TA们携手打响防疫战
    疫情下的传统商企自救|4个Tips搭建销量过亿直播间
    那些2019年会爆发的泛娱乐黑科技风口
    流量难、获客难、增长难?增长黑客思维“解救”B端业务
    【翻译】Facebook全面推出Watch Party,可多人线上同看直播视频
    深入浅出聊一聊Docker
    C++写日志方法调试
  • 原文地址:https://www.cnblogs.com/thythy/p/5493501.html
Copyright © 2011-2022 走看看