1. 程式人生 > >[BZOJ4700]適者(CDQ分治+DP/李超線段樹)

[BZOJ4700]適者(CDQ分治+DP/李超線段樹)

如果沒有秒殺,就是經典的國王遊戲問題,按t/a從小到大排序即可。

考慮刪除兩個數i<j能給答案減少的貢獻:S[i]*T[i]+P[i-1]*A[i]-A[i]+S[j]*T[j]+P[j-1]*A[j]-A[j]-T[i]*A[j]

其中P為T=(D-1)/ATK+1的字首和,S為A的字尾和。

我們設bi=S[i]*T[i]+P[i-1]*A[i]-A[i],考慮當i固定時,j可能取什麼值。

 

解法一:CDQ分治

不難發現,當i<j<k時k比j優的充要條件是b[k]-T[i]*A[k]>b[j]-T[i]*A[j],即T[i]<(b[k]-b[j])/(A[k]-A[j])。

不難看出斜率優化的模型,最後要最大化的是b[k]-T[i]*A[k]即所有點(A[k],b[k])以-T[i]的斜率投影到y軸上的最高點,於是答案一定在上凸殼上。

注意到這裡點的橫座標A和詢問斜率T均不單調,需要CDQ分治。

每次分治左半邊按T排序,右半邊按A排序並將凸包建好,然後左端點從前往後掃即可。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 4 typedef long long ll;
 5
using namespace std; 6 7 const int N=300010; 8 int n,m,q[N]; 9 ll A,B,ans,tot,c[N]; 10 struct P{ int x,y; ll z; }a[N],b[N]; 11 12 bool cmp(const P &a,const P &b){ return a.x*b.y>b.x*a.y; } 13 bool cmp2(const P &a,const P &b){ return a.y>b.y; } 14 bool cmp3(const P &a,const
P &b){ return a.x<b.x; } 15 ll calc(int x,int y){ return tot-a[x].z-a[y].z+a[y].x*a[x].y; } 16 double sl(int x,int y){ return (double)(a[y].z-a[x].z)/(a[y].x-a[x].x); } 17 18 void solve(int l,int r){ 19 if (l>=r) return; 20 int mid=(l+r)>>1; 21 solve(l,mid); solve(mid+1,r); 22 sort(a+l,a+mid+1,cmp2); sort(a+mid+1,a+r+1,cmp3); 23 int st=0,ed=0; 24 rep(i,mid+1,r){ 25 while (st<ed && sl(q[ed-1],q[ed])<sl(q[ed],i)) ed--; 26 q[++ed]=i; 27 } 28 rep(i,l,mid){ 29 while (st<ed && sl(q[st],q[st+1])>a[i].y) st++; 30 ans=min(ans,calc(i,q[st])); 31 } 32 } 33 34 int main(){ 35 freopen("bzoj4700.in","r",stdin); 36 freopen("bzoj4700.out","w",stdout); 37 scanf("%d%d",&n,&m); 38 rep(i,1,n) scanf("%d%d",&a[i].x,&a[i].y),A+=a[i].x,a[i].y=(a[i].y-1)/m+1; 39 sort(a+1,a+n+1,cmp); 40 rep(i,1,n){ 41 A-=a[i].x; B+=a[i].y; 42 a[i].z=a[i].x*(B-1)+A*(a[i].y); 43 tot+=a[i].x*(B-1); 44 } 45 ans=tot; solve(1,n); printf("%lld\n",ans); 46 return 0; 47 }

 

解法二:李超樹

不難發現,i固定時,j對答案減小的貢獻是b[j]-T[i]*A[j],這個式子可以理解為直線y=-A[j]*x+b[j]在T[i]處的總座標。

問題轉化為,i從大到小列舉,需要支援插入一條直線與線上查詢某個橫座標上所有直線總座標最大值。

動態維護凸包問題,顯然用李超樹。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define ls (x<<1)
 4 #define rs (ls|1)
 5 #define lson ls,L,mid
 6 #define rson rs,mid+1,R
 7 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 8 typedef long long ll;    
 9 using namespace std;
10 
11 const int N=300010;
12 int n,m,v[N<<2];
13 ll tot,ans,res,L[N],S[N],k[N],b[N];
14 struct P{ int a,t; }p[N];
15 bool operator <(const P &a,const P &b){ return a.a*b.t>b.a*a.t; }
16 bool pd(int x,int y,int p){ return k[x]*p+b[x]>k[y]*p+b[y]; }
17 ll calc(int x,int p){ return k[x]*p+b[x]; }
18 
19 void ins(int x,int L,int R,int p){
20     if (L==R){ if (pd(p,v[x],L)) v[x]=p; return; }
21     int mid=(L+R)>>1;
22     if (k[p]>k[v[x]]){
23         if (pd(p,v[x],mid)) ins(lson,v[x]),v[x]=p; else ins(rson,p);
24     }else{
25         if (pd(p,v[x],mid)) ins(rson,v[x]),v[x]=p; else ins(lson,p);
26     }
27 }
28 
29 void que(int x,int L,int R,int k){
30     res=max(res,calc(v[x],k));
31     if (L==R) return;
32     int mid=(L+R)>>1;
33     if (k<=mid) que(lson,k); else que(rson,k);
34 }
35 
36 int main(){
37     freopen("bzoj4700.in","r",stdin);
38     freopen("bzoj4700.out","w",stdout);
39     scanf("%d%d",&n,&m);
40     rep(i,1,n) scanf("%d%d",&p[i].a,&p[i].t),p[i].t=(p[i].t-1)/m+1;
41     sort(p+1,p+n+1);
42     rep(i,1,n) L[i]=L[i-1]+p[i].t;
43     for (int i=n; i; i--) S[i]=S[i+1]+p[i].a;
44     rep(i,1,n) k[i]=-p[i].a,b[i]=S[i]*p[i].t+L[i-1]*p[i].a-p[i].a,tot+=p[i].t*S[i]-p[i].a;
45     ins(1,1,n,n);
46     for (int i=n-1; i; i--)
47     res=0,que(1,1,n,p[i].t),ans=max(ans,b[i]+res),ins(1,1,n,i);
48     printf("%lld\n",tot-ans);
49     return 0;
50 }