Gym - 101981B Tournament (WQS二分+单调性优化dp)

题意:x轴上有n个人,让你放置m个集合点,使得每个人往离他最近的集合点走,所有人走的距离和最短。

把距离视为花费,设$dp[i][k]$表示前i个人分成k段的最小花费,则有递推式$dp[i][k]=min\{dp[j][k-1]+w(j,i)\}$,其中$w(j,i)$可以$O(1)$求出。

显然,如果考虑段数的话,光状态数就有n^2个,肯定行不通。不过这题的最优解对段数的函数是凸的,因此可以用WQS二分来打破段数的限制。

给每个集合点加上一个额外的花费c,然后忽略段数的限制,这样递推式就变成了$dp[i]=min\{dp[j]+w(j,i)\}+c$,这个递推式满足“决策单调性”,即如果i是由j转移而来,而i‘>i,则j‘>=j。这种dp是有一定的套路的,利用单调队列维护可能成为最优决策点的点以及它的左右边界,中间过程中需要不断地“掐头去尾”,及时弹出队首已经废掉的决策点,每push进一个结点,需要弹出队尾不如它优的决策点,并修改队尾的右边界,保证队首总是最优决策点。

然后在dp的过程中记录段数cnt[n],如果最优解分成了k段,那么dp[n]-k*c就是在划分为k段的条件下的最优解。根据k与m的大小关系进行二分,直到最优解恰好分成了m段为止。

你如果问为什么满足凸性和决策单调性?蒟蒻表示不会证,反正凭直觉猜就是了,或者打表~~

复杂度$O(nlognlogA)$,这道题还要和卡常斗智斗勇,算法常数过大会T,变量全开longlong也会T...

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int N=3e5+10,inf=0x3f3f3f3f;
 5 int n,m,hd,tl,a[N],q[N],cnt[N],L[N],R[N];
 6 ll S[N],dp[N],k;
 7 ll sum(int l,int r) {return S[r]-S[l-1];}
 8 ll w(int i,int j) {
 9     ++i;
10     return sum((i+j+2)>>1,j)-sum(i,(i+j-1)>>1)+k;
11 }
12 int fd(int j,int k) {
13     int ret=R[k]+1,l=L[k],r=R[k];
14     while(l<=r) {
15         int mid=(l+r)>>1;
16         if(dp[j]+w(j,mid)<=dp[k]+w(k,mid))ret=mid,r=mid-1;
17         else l=mid+1;
18     }
19     return ret;
20 }
21 int solve() {
22     hd=tl=0,L[0]=1,R[0]=n,q[tl++]=0;
23     for(int i=1; i<=n; ++i) {
24         for(; hd<tl&&R[q[hd]]<i; ++hd);
25         dp[i]=dp[q[hd]]+w(q[hd],i);
26         cnt[i]=cnt[q[hd]]+1;
27         for(; hd<tl&&dp[i]+w(i,L[q[tl-1]])<=dp[q[tl-1]]+w(q[tl-1],L[q[tl-1]]); --tl);
28         L[i]=(hd<tl?fd(i,q[tl-1]):i+1),R[i]=n;
29         if(hd<tl)R[q[tl-1]]=L[i]-1;
30         q[tl++]=i;
31     }
32     return cnt[n];
33 }
34 ll bi(ll l,ll r) {
35     ll ret;
36     while(l<=r) {
37         ll mid=(l+r)>>1;
38         k=mid;
39         if(solve()>=m)ret=dp[n]-m*k,l=mid+1;
40         else r=mid-1;
41     }
42     return ret;
43 }
44 int main() {
45     scanf("%d%d",&n,&m);
46     for(int i=1; i<=n; ++i)scanf("%d",&a[i]);
47     for(int i=1; i<=n; ++i)S[i]=S[i-1]+a[i];
48     printf("%lld\n",bi(0,S[n]+10));
49     return 0;
50 }

原文地址:https://www.cnblogs.com/asdfsag/p/11824458.html

时间: 2024-11-03 02:57:49

Gym - 101981B Tournament (WQS二分+单调性优化dp)的相关文章

决策单调性优化dp 专题练习

决策单调性优化dp 专题练习 优化方法总结 一.斜率优化 对于形如 \(dp[i]=dp[j]+(i-j)*(i-j)\)类型的转移方程,维护一个上凸包或者下凸包,找到切点快速求解 技法: 1.单调队列 : 在保证插入和查询的x坐标均具有单调性时可以使用 2.单调栈+二分:保证插入有单调性,不保证查询有单调性 3.分治+ 1 或 2:在每次分治时将\([l,mid]\)这段区间排序后插入,然后更新右区间\([mid+1,r]\)的答案 二.分治.单调队列维护有单调性的转移 (甚至还有分治套分治)

题解——[NOI2009]诗人小G 决策单调性优化DP

第一次写这种二分来优化决策单调性的问题.... 调了好久,,,各种细节问题 显然有DP方程: f[i]=min(f[j] + qpow(abs(sum[i] - sum[j] - L - 1))); 其中f[i]代表到了第i个句子的最小答案 qpow用于处理^p sum为前缀和 (同时为了处理句子之间的空格问题,我们在统计前缀和的时候就默认在句子后面加一个空格, 然后在计算的时候,由于每一行只有最后一个不用加空格,直接减掉这个多加的空格即可获得正确长度) 首先我们可以打表发现是满足决策单调性的,

关于二分栈优化DP算法的理解

引入 二分栈主要用来优化满足决策单调性的DP转移式. 即我们设\(P[i]\)为\(i\)的决策点位置,那么\(P[i]\)满足单调递增的性质的DP. 由于在这种DP中,满足决策点单调递增,那么对于一个点来说,以它为决策点的点一定是一段连续的区间. 所以我们可以枚举以哪个点作为决策点,去找到它所对应的以它为决策点的区间. 考虑如何找到一个点的区间: 可以发现,在当前情况下(枚举到以某个点作为决策点的情况下),该点所对应的区间一定为[L,N].(L可能等于N+1) 那么我们可以用一个栈来存储区间[

决策单调性优化dp

决策单调性: 对于一些dp方程,经过一系列的猜想和证明,可以得出,所有取的最优解的转移点(即决策点)位置是单调递增的. 即:假设f[i]=min(f[j]+b[j]) (j<i) 并且,对于任意f[i]的决策点g[i],总有f[i+1]的决策点g[i+1]>=g[i](或者<=g[i]) 那么,这个方程就具备决策单调性. 这个有什么用吗? 不懂具体优化方法的话确实也没有什么用.可能还是n^2的.只不过范围可能少了一些. 经典入门例题: Description: [POI2011]Ligh

Codeforces Round #344 (Div. 2) E. Product Sum 二分斜率优化DP

E. Product Sum Blake is the boss of Kris, however, this doesn't spoil their friendship. They often gather at the bar to talk about intriguing problems about maximising some values. This time the problem is really special. You are given an array a of

Codeforces 868F. Yet Another Minimization Problem【决策单调性优化DP】【分治】【莫队】

LINK 题目大意 给你一个序列分成k段 每一段的代价是满足\((a_i=a_j)\)的无序数对\((i,j)\)的个数 求最小的代价 思路 首先有一个暴力dp的思路是\(dp_{i,k}=min(dp_{j,k}+calc(j+1,i))\) 然后看看怎么优化 证明一下这个DP的决策单调性: trz说可以冥想一下是对的就可以 所以我就不证了 (其实就是决策点向左移动一定不会更优) 然后就分治记录当前的处理区间和决策区间就可以啦 //Author: dream_maker #include<bi

[BZOJ4709][JSOI2011]柠檬 决策单调性优化dp

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4709 我好弱啊QAQ,网上dalao们的题解根本看不懂啊,折腾了几个小时,有一点明白了. 首先要把朴素dp方程退出来. ①题目中说每次从序列的左右选一端取,但是如果你真的照着题目说的这样做我也不知道会怎么样.事实上很明显不管怎么取,最终答案都只跟划分出的是哪几个区间有关.所以不妨从左端开始取. ②如果取一个区间,区间第一个贝壳的大小和最后一个贝壳的大小不一样,那么很明显可以去掉第一个或最

Gym - 101234J Zero Game (单调队列优化dp)

题意:有一个长度为n的01序列,你可以移动k次,每次将一个数移到任意一个位置,求经过操作后区间连续最大的连续0的个数. “移动”操作看似情况很复杂,不好讨论,但其实无非就两种情况: 一.移动的是1:显然最优的策略是将1移动到最边上(相当于“移走”),目的是将两段连续的0合并. 二.移动的是0:最优策略是将小堆中的0移动到大堆里,目的是增加大堆中0的个数. 这样一来,情况就简单多了,问题转化成了求“将一段连续区间中的0合并,然后剩下的操作次数用于把其他地方的0引进来”的最优解,即求$min(max

[九省联考2018]林克卡特树(DP+wqs二分)

对于k=0和k=1的点,可以直接求树的直径. 然后对于60分,有一个重要的转化:就是求在树中找出k+1条点不相交的链后的最大连续边权和. 这个DP就好.$O(nk^2)$ 然后我们完全不可以想到,将best[k](选择k条链的答案)打表输出,更不可能然后作差分,发现得到的数组是递减的. 这说明:best[k]是一个上凸包. 于是我们可以二分一个斜率去切这个凸包(类似导数),根据切点横坐标与k的大小旋转直线(改变斜率). 考虑给你一个直线斜率k,怎么找到它和凸包的切点.实际上就相当于将这个凸函数减