luogu P5617 [MtOI2019]不可视境界线
题面传送门
这道题目的圆很特殊,在同一条直线上且半径都是\(r\),所以若干个圆的面积并就是这几个圆的面积和减去相邻圆面积并
那么显然有一个dp:设\(f_{i,j}\)为到了\(i\),选了\(j\)个圆的相邻圆面积并最小值。时间复杂度\(O(n^2k)\)
发现这个转移其实是\(O(n)\)的,式子比较复杂,不适合斜率优化,然后又满足四边形不等式所以是可以决策单调性的。
发现这个恰好\(k\)个很适合wqs二分的亚子,然后这个函数也是凸函数,所以可以WQS二分,预处理距离\(\leq 2r\)的圆的面积并就可以了,时间复杂度\(O(nlogrlogn)\)
code:
#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define re register
#define RI re int
#define ll long long
#define db double
#define lb long db
#define N 100000
#define K 20000
#define mod 1000000007
#define Mod (mod-1)
#define eps (1e-7)
#define U unsigned int
#define it iterator
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define d(x,y) (m*x+(y))
#define R(n) (rand()*rand()%(n)+1)
#define Pc(x) putchar(x)
#define calc(i,j) (A[j]-A[i]>=2*r?0:F[A[j]-A[i]])
using namespace std;
int n,k,r,A[N+5],La,Ne,H,T,L,R,M,g[N+5];db Ans=1e14,dp[N+5],F[K+5],Ls,Rs,Ms;const db pi=acos(-1);
struct Ques{int id,l,r;}Q[N+5];
I int check(db mid){
Q[H=T=1]=(Ques){0,1,n};for(RI i=1;i<=n;i++) {
while(H<=T&&Q[H].r<i) H++;dp[i]=dp[Q[H].id]+calc(Q[H].id,i)-mid,g[i]=g[Q[H].id]+1;Q[H].l=max(i,Q[H].l);
while(H<=T&&dp[Q[T].id]+calc(Q[T].id,Q[T].l)>=dp[i]+calc(i,Q[T].l))T--;if(H>T){Q[++T]=(Ques){i,i+1,n};continue;}
L=Q[T].l;R=Q[T].r+1;while(L+1<R) M=L+R>>1,(dp[Q[T].id]+calc(Q[T].id,M)>dp[i]+calc(i,M)?R:L)=M;Q[T].r=L;R<=n&&(Q[++T]=(Ques){i,R,n},0);
}int Id=0;for(RI i=1;i<=n;i++) dp[i]<dp[Id]&&(Id=i);return g[Id]>=k;
}
int main(){
freopen("1.in","r",stdin);
RI i,j,h;scanf("%d%d%d",&n,&k,&r);for(i=0;i<=2*r;i++) F[i]=r*r*2*acos(i/2.0/r)-i*sqrt(r*r-i*i/4.0);for(i=1;i<=n;i++) scanf("%d",&A[i]);
A[0]=A[1]-2*r-1;Ls=-pi*r*r;Rs=pi*r*r;while(Ls+eps<Rs) Ms=(Ls+Rs)/2,(check(Ms)?Rs:Ls)=Ms;check(Rs);for(i=0;i<=n;i++) Ans=min(Ans,dp[i]);printf("%.9lf\n",k*pi*r*r-Rs*k-Ans);
}