【好题】导数+统计贡献+扫描—— 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); }