1. 程式人生 > >BZOJ 4951 [WF2017]Money for Nothing (決策單調優化DP)

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 }