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
",ans);
79     return 0;
80 }
原文地址:https://www.cnblogs.com/guapisolo/p/10257311.html