JZOJ 5481. 【清华冬令营2018模拟】第二题

题目大意

给定一个长度为L的环,此环上有n个点,d[i]表示i-1和i的距离。
每个点有一权值w[i],但是w[n]为在[a,b]之间等概率分布的一个数。
假设选择x为特殊点,那么对答案的贡献为sum[x].
设x到i的两条路径长度分别为l1,l2.

sum[x]=Σni=1w[i]l1l2

选择sum[x]最小的点x作为特殊点。
那么,每个点被选为特殊点的概率为多少?
数据范围
对于20%的数据,n≤1000。
对于另20%的数据,a=b。
对于另20%的数据,w[1]=w[2]=……=w[n-1] < <script type="math/tex" id="MathJax-Element-16"><</script>a≤b,d[i]=1。
对于100%的数据n≤100000,a≤b≤10000,w[i]≤10000,d[i]≤10。

题解

由于w[n]是未知的,设它为x,那么sum[i]可以用k[i]*x+b[i]来表示。
qzd[i]=Σij=1d[j]
将式子展开,即

sum[i]=Σi1j=1w[j](qzd[i]qzd[j])(L(qzd[i]qzd[j]))+Σn1j=i+1w[j](qzd[j]qzd[i])(L(qzd[j]qzd[i]))+w[n](qzd[n]qzd[i])(L(qzd[n]qzd[i]))

显然,$k[i]=(qzd[n]-qzd[i])*(L-(qzd[n]-qzd[i]))

b[i]=Σi1j=1w[j](qzd[i]qzd[j])(L(qzd[i]qzd[j]))
        +Σn1j=i+1w[j](qzd[j]qzd[i])(L(qzd[j]qzd[i]))
n1000 ,那么 O(n2) 直接算 k[i],b[i] 就好了。
问题是怎么将时间复杂度均摊成 O(1)
显然计算k可均摊 O(1) ,直接将式子摆出来就OK了
均摊 O(1) ,就是说在O(1)时间内通过求出 b[i]b[i1]
然后得到n条直线。
接着维护个上凸壳就好了。
由于维护凸壳方面做得不够好,所以要加强练习。
首先我们要找当x值一定时,所有直线的y的min,那么按照斜率k从大到小排序,然后按照交点的横坐标建立单调栈。画画图,考虑淘汰哪条直线。

!!!要注意的地方


①这个式子我一早就推出来了,但是浪费了很多的改题时间。为什么?因为自己的一些毛病,自以为是将符号弄反,可能将某些变量搞错。
因此,在做题的时候,确定这个式子是对的,那就将它留着,然后一个一个将里面的变量拆到 Σ 外面去。
注意读入的每一个变量类型,不要被样例坑!!!!!
③这是一次维护凸壳的好机会,不要忘记用单调栈维护了!!!!!

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 100010
#define DB double
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
struct note{
    DB k,b;
    int id;
};note li[N];
struct note1{
    DB data,cnt;
    int wz;
};note1 st[N];
DB a,b;
int i,j,l,r,n,m,cnt;
DB qz[N],qzd[N],d[N],w[N],c[N],k[N];
int ans[N],top;
DB temp,tl,tr,mx,L,sum,D,s2,last;
DB yy[N],Ans[N];
DB dis(int x,int y){return qzd[y]-qzd[x];}
DB jd(note x,note y){return 1.0*(y.b-x.b)/(x.k-y.k);}
bool cmp(note x,note y){return x.k>y.k||(x.k==y.k&&x.b<y.b)||(x.k==y.k&&x.b==y.b&&x.id<y.id);}
void put_in(int x,int y){
    if(li[x].k==li[st[top].wz].k)return;
    while(top>1 && st[top].data-jd(li[st[top].wz],li[x])>1e-7)top--;
    st[++top].wz=x;
    st[top].cnt=y;
    if(top>1)st[top].data=jd(li[st[top-1].wz],li[st[top].wz]);
}
int main(){
    scanf("%d%lf%lf\n",&n,&a,&b);
    fo(i,1,n-1)scanf("%lf",&w[i]),sum+=w[i];
    fo(i,1,n)scanf("%lf",&d[i]),L+=d[i];
    fo(i,2,n-1)tr+=dis(1,i)*w[i];
    fo(i,1,n){
        qzd[i]=qzd[i-1]+d[i];
        qz[i]=qz[i-1]+w[i];
    }
    fo(i,1,n-1)s2+=qzd[i]*w[i];
    fo(i,1,n-1){
        k[i]=dis(i,n)*(L-dis(i,n));
        if(i==1){
            fo(j,2,n-1)c[1]+=dis(1,j)*(L-dis(1,j))*w[j];
        } else{
            D=qzd[i]-qzd[i-1];
            temp=D*(L-D)*(w[i-1]-w[i]);
            temp+=D*D*(sum-w[i]-w[i-1]);
            temp+=D*L*(qz[i-2]-qz[n-1]+qz[i]);
            tl=w[i-1]*D-qzd[i]*qz[n-1]+s2;
            temp+=2*D*tl;
            c[i]=c[i-1]+temp;
        }
    }
    fo(i,1,n-1)c[n]+=dis(i,n)*(L-dis(i,n))*w[i];
    if(a==b){
        fo(i,1,n){
            yy[i]=k[i]*a+c[i];
            if(i==1)mx=yy[i];
            if(yy[i]<mx){
                ans[ans[0]=1]=i;
                mx=yy[i];
            } else
            if(yy[i]-mx<1e-7)ans[++ans[0]]=i;
        }
        fo(i,1,n)
            if(yy[i]-mx>1e-7)Ans[i]=0;else Ans[i]=1.0/ans[0];
        fo(i,1,n)printf("%.3lf\n",Ans[i]); 
        return 0;
    }
    fo(i,1,n)li[i].k=k[i],li[i].b=c[i],li[i].id=i;
    sort(li+1,li+n+1,cmp);
    for(l=1,r=0;l<=n;l=r+1){
        while(li[l].k==li[r+1].k&&li[l].b==li[r+1].b)r++;
        put_in(l,r-l+1);
    }
    st[top+1].data=2147483647.0;
    last=a;
    fo(i,2,top+1)
        if(st[i].data-a>=1e-7){
            if(st[i].data-b>1e-7){
                Ans[li[st[i-1].wz].id]=(b-last)/(b-a)/st[i-1].cnt;
                fo(j,st[i-1].wz+1,n){
                    if(li[j].k!=li[j-1].k||li[j].b!=li[j-1].b)break;
                    Ans[li[j].id]=Ans[li[st[i-1].wz].id];
                }
                break;
            }
            Ans[li[st[i-1].wz].id]=(st[i].data-last)/(b-a)/st[i-1].cnt;
            fo(j,st[i-1].wz+1,n){
                if(li[j].k!=li[j-1].k||li[j].b!=li[j-1].b)break;
                Ans[li[j].id]=Ans[li[st[i-1].wz].id];
            }
            last=st[i].data;
        }
    fo(i,1,n)printf("%.3lf\n",Ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值