gym102433 J-Interstellar Travel 计算贡献
gym102433 J-Interstellar Travel
题意
给定\(n\)个实数三元组\((t_i,s_i,a_i)\),让你选定一个\(a\)使得\(\sum_{i=1}^{n}max(0,t_i-s_i\cdot dist(a_i,a))\)最大,其中\(dist(a_i,a)=min(2\pi-|a-a_i|,|a-a_i|)\)。
\(1\le n\le 10^5,0 < t\le 1000,0\le s\le 100,0\le a\le 2\pi\)
分析
对于每个三元组可以分成四段线性函数
对于每一段看做\(y=s\cdot a+t\),那么我们只需要将每一段的端点和斜率保存起来,最大值一定在某个端点处取得,按端点排下序,对于排序后的相邻的端点之间计算一下贡献取最大值即可。
Code
#include<bits/stdc++.h>
#define rep(i,x,n) for(int i=x;i<=n;i++)
#define per(i,n,x) for(int i=n;i>=x;i--)
#define sz(a) int(a.size())
#define rson mid+1,r,p<<1|1
#define pii pair<double,double>
#define lson l,mid,p<<1
#define ll long long
#define pb push_back
#define mp make_pair
#define se second
#define fi first
using namespace std;
const double eps=1e-8;
const double pi=acos(-1);
const int mod=1e9+7;
const int N=1e5+10;
const int inf=1e9;
int n;
vector<pii>q;
double s,t,a;
double calc(double x){
return t-s*(min(2*pi-abs(a-x),abs(a-x)));
}
void add(double l,double r,double pa,double s){
l=max(l,0.0);
r=min(r,2*pi);
if(l>r) return;
double vl=calc(l),vr=calc(r);
if(vl<=0&&vr<=0) return;
if(vl>=0&&vr>=0){
q.pb(mp(l,s));
q.pb(mp(r,-s));
}else if(vl>=0){
double x=-t/s+pa;
q.pb(mp(l,s));
q.pb(mp(x,-s));
}else if(vr>=0){
double x=-t/s+pa;
q.pb(mp(x,s));
q.pb(mp(r,-s));
}
}
int main(){
cin>>n;
double ret=0;
rep(i,1,n){
scanf("%lf%lf%lf",&t,&s,&a);
ret+=max(0.0,calc(0));
add(a-2*pi,a-pi,a-2*pi,-s);
add(a-pi,a,a,s);
add(a,a+pi,a,-s);
add(a+pi,a+2*pi,2*pi+a,s);
}
sort(q.begin(), q.end());
double pr=0,k=0,ans=ret;
for(pii x:q){
ret+=k*(x.fi-pr);
k+=x.se;
pr=x.fi;
ans=max(ans,ret);
}
printf("%.6f\n",ans);
return 0;
}