【好题】导数+统计贡献+扫描—— icpc PNWRC 2019

这题的思路很好

首先不难发现,在特定的角度范围内,所求值的函数是线性的,这暗示了最大值必定在 等于某个给定
的角度 时取得。直接暴力地计算每一个值是显然不可取的,但我们可以换一种思路,预先计算每个给
定角度所能对答案贡献的值的函数的导数变化的角度,然后将这些角度排序,开始遍历,维护到达每个
角度时答案和答案的变化量。这样,就可以一遍求出所有的值,取最大值即可,另外,由于角度的dist计算比较特殊,我们向前向后再扩充一个pi即可
#include<bits/stdc++.h>
using namespace std;
#define N 500005
#define db double

const db pi = acos(-1.0);

int n,m;
db T[N],s[N],a[N],ans,init;

struct Seg{
    Seg(){}
    Seg(db p,db d):p(p),d(d){}
    db p,d;
}seg[N<<2];
int cmp(Seg a,Seg b){
    return a.p<b.p;
}

void add(int i,db L,db R,db T,db s,db a){//区间L,R里加一段si 
    //Ti+si(a-ai)
    db fL=T+s*(L-a);
    db fR=T+s*(R-a);
    db mi=min(fL,fR);
    L=max(L,0.0);R=min(R,2*pi);
    if(L>R)return;
    if(s==0 || mi>=0){//整段都可以加 
        seg[++m]=Seg(L,s);
        seg[++m]=Seg(R,-s);
    }else{//只能加一部分 
        db zero=-T/s+a;
        zero=max(zero,0.0);
        if(fL>=0){
            seg[++m]=Seg(L,s);
            seg[++m]=Seg(zero,-s);
        }else if(fR>=0){
            seg[++m]=Seg(zero,s);
            seg[++m]=Seg(R,-s);
        }
    }
}

int main(){
    //freopen("7.in","r",stdin);
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>T[i]>>s[i]>>a[i];
        init+=max(0.0,T[i]-s[i]*min(a[i],2*pi-a[i]));
        add(i,a[i]-2*pi,a[i]-pi,T[i],-s[i],a[i]-2*pi);
        add(i,a[i]-pi,a[i],T[i],s[i],a[i]);
        add(i,a[i],a[i]+pi,T[i],-s[i],a[i]);
        add(i,a[i]+pi,a[i]+pi*2,T[i],s[i],a[i]+2*pi);
    }
    
    sort(seg+1,seg+1+m,cmp);
    ans=max(ans,init);
    
    /*for(int i=1;i<=m;i++){
        cout<<seg[i].p<<" "<<seg[i].d<<'\n';
    }*/
    
    db now=0;
    for(int i=1;i<m;i++){
        now+=seg[i].d;
        init+=now*(seg[i+1].p-seg[i].p);
        ans=max(ans,init);
    }
    printf("%.6lf\n",ans);
}

 

posted on 2020-05-07 09:28  zsben  阅读(145)  评论(0编辑  收藏  举报

导航