BZOJ 4951 [WF2017]Money for Nothing (決策單調優化DP)
題目大意:略 題目傳送門
不愧是$World final$的神題,程式碼短,思維強度大,細節多到吐..調了足足2h
貪心
我們利用貪心的思想,發現有一些工廠/公司是非常黑心的
以工廠為例,對於一個工廠$i$,如果存在一個工廠$j$,$d_{j}<d_{i},p_{j}<p_{i}$,即出貨比i早,而且比i便宜
那麼不論我們選擇任何消費公司,都一定會選$j$而不是選$i$
消費公司也用類似的貪心方法,消去那些黑心公司
以$d$為$x$軸,$p$為$y$軸,我們得到了許多在二維平面上的點,保證$d_{i}$遞增,$p_{i}$遞減
這一步貪心可以用單調棧實現
決策單調性
發現我們篩出來這些點以後,並沒有什麼卵用
假如把這個問題看成一個幾何問題,題目轉化為,能作為右上角的點視為白點,能作為左下角的點視為黑點,給定很多黑點白點,求白點作為右上角,黑點作為左下角,圍成的矩形的面積最大值,且點集內的保證$d_{j}<d_{i},p_{j}<p_{i}$
以下討論中,點的編號隨橫座標$d$增大而增大
大膽地猜測一下,假設我們選擇一個左下角黑點$i$,它對應的最優右上角白點是$j$,那麼當$j-1$不能和$i$構成最優解時,點$i+1$能否和$j-1$構成最優解呢?
答案是不能,下面給出證明
我們已知$(x_{j}-x_{i})(y_{j}-y_{i})>(x_{j-1}-x_{i})(y_{j-1}-y_{i}),x_{i}<x_{i+1},y_{i}>y_{i+1},x_{j}<x_{j+1},y_{j}>y_{j+1}$
假如能構成最優解,那麼$(x_{j-1}-x_{i+1})(y_{j-1}-y_{i+1})>(x_{j}-x_{i+1})(y_{j}-y_{i+1})$
把兩式展開,消項可得
$x_{j}\cdot y_{j}-x_{i}\cdot y_{j}-x_{j}\cdot y_{i}>x_{j-1}\cdot y_{j-1}-x_{i}\cdot y_{j-1}-x_{j-1}\cdot y_{i}$
$x_{j-1}\cdot y_{j-1}-x_{i+1}\cdot y_{j-1}-x_{j-1}\cdot y_{i+1}>x_{j}\cdot y_{j}-x_{i+1}\cdot y_{j}-x_{j}\cdot y_{i+1}$
不等號方向相同,兩式相加,消去相同項,可得
$-x_{i}\cdot y_{j}-x_{j}\cdot y_{i}-x_{i+1}\cdot y_{j-1}-x_{j-1}\cdot y_{i+1}>-x_{i}\cdot y_{j-1}-x_{j-1}\cdot y_{i}-x_{i+1}\cdot y_{j}-x_{j}\cdot y_{i+1}$
合併,整理可得
$(x_{i+1}-x_{i})(y_{j-1}-y_{j})+(y_{i}-y_{i+1})(x_{j}-x_{j-1})<0$
由於$x_{i}<x_{i+1},y_{i}>y_{i+1},x_{j}<x_{j+1},y_{j}>y_{j+1}$,上述式子一定大於$0$,所以這種情況不存在
分治求解
我們剛剛發現了這個優美的性質,接下來該如何利用這個性質求解呢?
考慮分治,用類似於整體二分的方法,把右上角的點看成詢問,左下角的點視為操作(決策)
當前的詢問區間是$[l1,r1]$,決策區間是$[l2,r2]$
現在選擇了詢問區間的重點$mid$,遍歷$[l2,r2]$找到$mid$的決策$p$
根據決策單調性,$[l1,mid-1]$的決策一定屬於$[l2,p]$,$[mid+1,r1]$的決策一定屬於$[p,r2]$
類似於整體二分的方法,遞迴即可
繁多的細節
你作為中國代表隊的一員參加了$ACM$,成功晉級$World Final$,然後你一眼看出了貪心,決策單調性,以及靠譜的分治演算法,你搶過隊友的鍵盤開始碼這道題
不過這道題程式碼實現的細節還挺多的
首先,分治演算法中,$mid$的決策$p$可能有多個,它們中的某幾個可能同時作為[l1,mid-1]和[mid+1,r1]的決策
所以我們要把所有的決策$p$全都推到兩邊分治
但如果$mid$找不到決策怎麼辦?
找到最大的可能作為$[l1,mid-1]$和$[mid+1,r1]$的決策區間,繼續遞迴
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define N1 500010 5 #define ll long long 6 #define dd double 7 #define inf 1333333333 8 using namespace std; 9 10 int gint() 11 { 12 int ret=0,fh=1;char c=getchar(); 13 while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();} 14 while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();} 15 return ret*fh; 16 } 17 18 int n,m,nn,mm; int stk[N1],tp; ll ans; 19 struct node{int x,v;}a[N1],b[N1],tmp[N1]; 20 int cmp1(node s1,node s2){ if(s1.x!=s2.x) return s1.x<s2.x; return s1.v<s2.v; } 21 int cmp2(node s1,node s2){ if(s1.x!=s2.x) return s1.x<s2.x; return s1.v>s2.v; } 22 23 int de; 24 void solve(int l1,int r1,int l2,int r2) 25 { 26 if(l1>r1||l2>r2) return; 27 int mid=(l1+r1)>>1,i,S=-1,E=-1; ll ma=-1,tmp; 28 for(i=l2;i<=r2;i++) 29 { 30 tmp=1ll*(b[mid].v-a[i].v)*(b[mid].x-a[i].x); 31 if(b[mid].x<a[i].x||b[mid].v<a[i].v) tmp=-inf; 32 if(tmp>ma) ma=tmp,S=E=i; 33 else if(tmp==ma) E=i; 34 } 35 if(S==-1&&E==-1) 36 { 37 de=1; 38 if(l1<=mid+1&&mid+1<=r1) 39 for(i=l2;i<=r2;i++) if(b[mid+1].v>=a[i].v){ 40 solve(mid+1,r1,i,r2); break; } 41 if(l1<=mid-1&&mid-1<=r1) 42 for(i=r2;i>=l2;i--) if(b[mid-1].x>=a[i].x){ 43 solve(l1,mid-1,l2,i); break; } 44 return; 45 } 46 ans=max(ans,ma); 47 solve(l1,mid-1,l2,E); solve(mid+1,r1,S,r2); 48 } 49 50 int main() 51 { 52 scanf("%d%d",&m,&n); 53 int i; 54 for(i=1;i<=m;i++){ a[i].v=gint(); a[i].x=gint(); } 55 for(i=1;i<=n;i++){ b[i].v=gint(); b[i].x=gint(); } 56 57 sort(a+1,a+m+1,cmp1); 58 for(tp=0,i=m;i;i--) 59 { 60 while(tp>=1&&a[i].v<=a[stk[tp]].v) tp--; 61 stk[++tp]=i; 62 } 63 for(i=1;i<=tp;i++) tmp[i]=a[stk[i]]; 64 for(i=1;i<=tp;i++) a[i]=tmp[tp-i+1]; 65 mm=tp; 66 67 sort(b+1,b+n+1,cmp2); 68 for(tp=0,i=1;i<=n;i++) 69 { 70 while(tp>=1&&b[i].v>=b[stk[tp]].v) tp--; 71 stk[++tp]=i; 72 } 73 for(i=1;i<=tp;i++) tmp[i]=b[stk[i]]; 74 for(i=1;i<=tp;i++) b[i]=tmp[i]; 75 nn=tp; 76 77 solve(1,nn,1,mm); 78 printf("%lld\n",ans); 79 return 0; 80 }