写一写要讲什么免得忘记了。DP的优化。

大概围绕着"是什么","有什么用","怎么用"三个方面讲.

主要是《算法竞赛入门经典》里的题目讲解,但是有些过于简单的删去了,添加了一些不怎么简单的省选题目作为例子

这里的DP优化都是涉及到O(nk)到O(nk-1)方法比较巧妙也有用到数学里面的定理之类。

所以秉着由易到难的原则,安排内容如下:

专题1:动态规划基础知识和计数DP、数位DP(几大类DP的类型介绍)

专题2:DP的简单优化(稍微提两句mjy大佬的任务)

专题3:单调队列优化DP和斜率优化(这个比较难也比较重要) blog

专题4:四边形不等式优化DP

专题5:习题课

专题1:动态规划基础知识和计数DP、数位DP(几大类DP的类型介绍)

专题2:DP的简单优化(稍微提两句mjy大佬的任务)

专题3:单调队列优化DP和斜率优化(这个比较难也比较重要)

斜率优化DP   blog

将这个DP的优化方法之前我们必须看一个例子,优化的前提一直是暴力DP不错!

P3195 [HNOI2008]玩具装箱TOY

这个DP方程非常好想,F[i]从第1个到第i个物品放在箱子里的最小花费。

转移从第j个物品开始考虑在第j个物品放完之后的(i+1)到第j个物品放在一个容器中,每次决策一次那么得出方程式

为了方便起见我们这里的L++,然后用sum[x]表示C的前缀和那么DP方程就可以改写为:

然后我们发现对于确定的i,sum[i]+i的值是一定的,我们用s[x]表示sum[i]+i

进一步改写DP方程:

于是我们这个DP方程就显的优美了,不妨把暴力的代码打出来把:

# include<bits/stdc++.h>
# define int long long
# define SQR(x) ((x)*(x))
using namespace std;
const int MAXN=1e5+;
int s[MAXN],f[MAXN];
int n,L;
signed main()
{
scanf("%lld%lld",&n,&L); L++;
int t;
for (int i=;i<=n;i++)
scanf("%lld",&t),s[i]=s[i-]+t;
for (int i=;i<=n;i++) s[i]+=i;
memset(f,0x3f,sizeof(f)); f[]=;
for (int i=;i<=n;i++)
for (int j=;j<i;j++)
f[i]=min(f[i],f[j]+SQR(s[i]-s[j]-L));
printf("%lld\n",f[n]);
return ;
}

我们发现这样的算法时间完全承受不了,我们考虑优化!!!

优化用到的正是斜率优化。

为了O(1)转移我们必须寻求一种方法来找到最优的转移方案,

我们不妨把式子化简一下

对于当前最优的决策方案Fi,我们的每一个j都可以表示一个Fi的取值,这里取到最值,这和直线非常相似我们不妨把带有j的当做变量分离一下试试

b  +   k  *  x       =     y

我们发现这样一个神奇的式子,对于每一个j的取值都有一个在J(sj+L,fj+si2+(sj+L)2)与之对应,这个J就是坐标轴上离散的一个点,

就好比对于所有决策状态中的点J集合,一条直线(斜率K=2si已固定)经过这个J集合中至少一个点,使其截距b,尽可能小。

观察到题目中的c[i]都是正数意味着k=2*s[i]必然单调递增,我们承认的一个事实是在平面直角坐标系中一条直线k的值越大越陡,截距b越小

考虑怎样一个数据结构可以维护这样一个,单调递增k的特性呢?答案显然是单调队列,我们只要维护一个下凸包即可。

具体的解释是这样,考虑F[i]的斜率2*si如显然AB的斜率比F[i]的斜率小那么显然,A就是一个废弃的点(由于B的存在我宁可连B也不连A),我们就可以把它弹掉。

对于更新过的坐标集,第一个点的显然是最优的,由于满足下凸的性质,直线斜率不变,那么截距只能越来越大,这时候更新答案,更新完毕之后由于产生一个新的决策点J

我们需要对前面的点做一遍检查

对于新加进来的这个决策点new(就是当前的最优值),我们判断他是不是有资格作为后续DP状态的来源点,

如果new这个点和A这个点的斜率比BC的斜率还要小,那么BC这两个点将会被清除由于后续来的斜率线段一定会选择过new而不是B或者C

这也是基于上面的下凸包的性质。

提醒一下对于当前需要转移的i,我们可以不作记录的原因在于

对于每一个和i有关的常数我们都会在作差之中消除,我们可以不用理他(抵消!),这样程序就没有了i的干扰了!

Code:

# include<bits/stdc++.h>
# define int long long
# define SQR(x) ((x)*(x))
using namespace std;
const int MAXN=5e4+;
int sum[MAXN],F[MAXN],c[MAXN],s[MAXN],q[MAXN];
int n,L;
inline double X(int j){ return (double)s[j];}
inline double Y(int j){ return (double)F[j]+(s[j]+L)*(s[j]+L);}
//和i无关的每一个j点计算出他的横坐标和纵坐标
inline double R(int i,int j){return (Y(j)-Y(i))/(X(j)-X(i));}
//i下的两点斜率
# define Empty (head>=tail)
signed main()
{
scanf("%lld%lld",&n,&L); L++; sum[]=;
for (int i=;i<=n;i++)
scanf("%d",&c[i]),
sum[i]=sum[i-]+c[i],
s[i]=sum[i]+i;
int head=,tail=; q[]=;
//涉及到取两个元素的队列还是手打比较好
for (int i=;i<=n;i++) {
while (!Empty&&R(q[head],q[head+])<*s[i]) head++;
//不满足下凸的性质队头出
int j=q[head]; F[i]=F[j]+SQR(s[i]-s[j]-L);
//转移
while (!Empty&&R(q[tail-],q[tail])>R(q[tail],i)) tail--;
//不满足下凸性质的队尾出
q[++tail]=i;
//加入一个新的决策i
}
printf("%lld\n",F[n]);
return ;
}
//这个板子会在后面经常用到

这里还需要提高一下,我们其实不需要吧这个直线写出来就可以知道斜率,这样减少思维难度。

是这个方程,我们不妨考虑一个决策k在另一个决策j之前,但是k没有j优秀(对于更新外部循环变量i来说),

即 

所以k这个决策无用抛弃。

可以化简为左边是si和sj或sk乘积形式除过去,就可以得到斜率

这个式子本质上是和上面是一样的,和R没有什么区别。

维护的话相似。

P2120 [ZJOI2007]仓库建设

考虑最简单的DP方程:

f[i]从山顶(1号)到第i号放完的最小代价

考虑f[i]从j转移过来。

设Wk表示如果将i这个地点作为建站处那么对于k<i的任意一个点,其代价

那么从1-j 已经处理完毕,考虑 j+1 到 i 这些物品的结构

转移方程:

对于需要转移的每一个x[i]不变,转移方程可以改写为

前缀和处理 -x[i]*p[i]和p[i]的前缀和分别为 g[i] 和 P[i]

这样复杂度降到了O(n^2)

帖下代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=1e6+;
int f[MAXN],x[MAXN],p[MAXN],P[MAXN],c[MAXN],g[MAXN];
int n;
signed main()
{
scanf("%lld",&n);
for (int i=;i<=n;i++)
scanf("%lld%lld%lld",&x[i],&p[i],&c[i]),
P[i]=P[i-]+p[i],g[i]=g[i-]-x[i]*p[i];
memset(f,0x3f,sizeof(f)); f[]=;
for (int i=;i<=n;i++)
for (int j=;j<i;j++)
f[i]=min(f[i],f[j]+x[i]*P[i]-x[i]*P[j]+g[i]-g[j]+c[i]);
printf("%lld\n",f[n]);
return ;
}

接下来将斜率优化的部分:

f[i]=f[j]+x[i]*P[i]-x[i]*P[j]+g[i]-g[j]+c[i]

f[i]+x[i]*P[j] = f[j] + x[i]*P[i] +g[i] - g[j] + c[i]

b +   k   *  x  =  y

由于斜率单调递增,那么x[i]单调递增所以斜率单调递增,所以处理方法同上!

#include <bits/stdc++.h>
#define int long long
#define Empty (head>=tail)
using namespace std;
const int MAXN=1e6+;
int f[MAXN],x[MAXN],p[MAXN],P[MAXN],c[MAXN],g[MAXN],q[MAXN];
double X(int j) { return (double)P[j];}
double Y(int j) { return (double)f[j]-(double)g[j];}
double R(int i,int j){return (double)(Y(i)-Y(j))/(X(i)-X(j));}
int n;
signed main()
{
scanf("%lld",&n);
for (int i=;i<=n;i++)
scanf("%lld%lld%lld",&x[i],&p[i],&c[i]),
P[i]=P[i-]+p[i],g[i]=g[i-]-x[i]*p[i];
int head=,tail=; q[]=;
for (int i=;i<=n;i++) {
while (!Empty&&R(q[head],q[head+])<x[i]) head++;
int j=q[head]; f[i]=f[j]+x[i]*P[i]-x[i]*P[j]+g[i]-g[j]+c[i];
while (!Empty&&R(q[tail-],q[tail])>R(q[tail],i)) tail--;
q[++tail]=i;
}
printf("%lld\n",f[n]);
return ;
}

P3628 [APIO2010]特别行动队

先考虑暴力DP+前缀和优化!

设F[i]表示前i个士兵安排任务最大化战斗力,sum[x]表示x的前缀和数组

对于这个式子可以用前缀和表示,用F(x)=A*x*x+B*x+C代换可知

依然考虑斜率优化下,还是写成斜率的形式

依旧把有斜率的东西放到左边,右边保留一个解析式,就像这样:

b  +   k     *   x      =  y

这里是斜率k单调递减然后求fi最大值,其实只要向上面一样维护一个上凸包即可!

代码其实只要改两个符号就差不多了,理解就是取反然后按照斜率递增求Min一样就行。

code:

# include <bits/stdc++.h>
# define int long long
# define Empty (head>=tail)
using namespace std;
const int MAXN=2e6+;
int sum[MAXN],f[MAXN],q[MAXN*],A,B,C,n;
double X(int j){return sum[j];}
double Y(int j){return (double)f[j]+A*sum[j]*sum[j]-B*sum[j];}
double R(int i,int j){return (double)(Y(i)-Y(j))/(double)(X(i)-X(j));}
int Fun(int x) {return A*x*x+B*x+C;}
signed main()
{
scanf("%lld",&n);
scanf("%lld%lld%lld",&A,&B,&C);
int t;
for (int i=;i<=n;i++)
scanf("%lld",&t),sum[i]=sum[i-]+t;
int head=,tail=; q[]=;
for (int i=;i<=n;i++) {
while(!Empty&&(R(q[head+],q[head])>*A*sum[i])) head++;
int j=q[head]; f[i]=f[j]+Fun(sum[i]-sum[j]);
while (!Empty&&(R(q[tail],q[tail-])<R(q[tail],i))) tail--;
q[++tail]=i;
}
printf("%lld\n",f[n]);
return ;
}

到这里我们已经完成了斜率优化的入门题型这里给出几个练习,有助于能力提升:

专题4:四边形不等式优化DP

四边形不等式:相交小于等于包含

设w(x,y)是定义在Z上的二元函数,对于a<=b<=c<=d属于Z,都有w(a,d)+w(b,c)>=w(a,c)+w(b,d)

或者定义a<b,有w(a,b+1)+w(a+1,b)>=w(a,b)+w(a+1,b+1)

这两种定义是等价的!

证明:对于a<c,有w(a,c+1)+w(a+1,c)>=w(a,c)+w(a+1,c+1) (第2种定义)

   对于a+1<=c,有w(a+1,c+1)+w(a+2,c)>=w(a+1,c)+w(a+2,c+1) (第2种定义)

   两式相加,得:w(a+1,c+1)+w(a+2,c)+w(a,c+1)+w(a+1,c)>=w(a+1,c)+w(a+2,c+1) + w(a,c)+w(a+1,c+1)

   消去相同项得:w(a+2,c)+w(a,c+1)>=w(a+2,c+1) + w(a,c)

   同理对于任意的a<=b<=c有w(a,c+1)+w(b,c)>=w(a,c)+w(b,c+1)

   同理对于任意的a<=b<=c<=d都有w(a,d)+w(b,c)>=w(a,c)+w(b,d)

证毕。

一维线性DP的四边形不等式优化:

对于形如  的一维线性DP方程,记录P[i]表示F[i]取到最小值的j,

若P[i]单调不减,则F具有决策单调性

定理:若val满足四边形不等式即val为凸(以后为了方便,满足四边形不等式的性质一律叫凸) 则F具有决策单调性

证明:令i在[1,N],j在[0,P[i]-1],i’在[i+1,N]

根据P[i]最优性,得F[P[i]]+val(P[i],i)<=F[j]+val(j,i)

由于val满足四边形不等式,有val(j,i)+val(P[i],i')<=val(j,i')+val(P[i],i)【相交小于等于包含】

两式相加,得:F[P[i]]+val(P[i],i)+val(j,i)+val(P[i],i')<=F[j]+val(j,i)+val(j,i')+val(P[i],i)

消去相同项,得:F[P[i]]+val(P[i],i')<=F[j]+val(j,i)  

对于i'的最优决策P[i']在[P[i],i']不可能小于P[i],即P[i']>=P[i]

所以F满足决策单调性

在循环的任意时刻,数组中的情况一定是形如

由于决策单调则j1<j2<j3<j4<j5

求出F[i]后考虑i可能作为F[i'] (i'>i)的决策,那借用单调队列的思想考虑一个位置pos,之前的决策都比i好,之后的决策都比i差,

我们需要快速找到上述位置并把之后的所有元素改为i,把[pos,i]改为i,

假设我们的位子在j3(中间那个),那么处理后的数组就变为:

显然直接修改效率太低,我们在队列中用若干个三元组(j,l,r)表示数组的[l,r]最优决策都是j

另外队列中无需保留小于P[1~i-1]的部分,(由于F的决策单调性)

队列头部就是最优决策

算法:

  1. 检查队头(j0,l0,r0),若r0<=i-1,删除队头,否则l0=i
  2. 取出队头的决策j作为最优决策,状态转移求出F[i]
  3. 尝试插入新决策i:
    • (1)取出队尾(jt,lt,rt)
    • (2)对于F[lt]来说i是比jt更优的决策(由于决策单调对于lt来说i都优于此时的决策那么在队尾整个区间都差于此时的决策),pos=l删除队尾,goto(1)
    • (3)对于F[rt]来说i不如jt更优 goto(5)
    • (4)不满足(2)和(3)的,在[lt,rt]二分查找到pos,使之前的决策比i更优,之后的决策i更优(就说对于F[mid]来说,i决策比jt决策更优最小化mid),goto(5)
    • (5)把(i,pos,N)插入队尾

[诗人小G]

# include <bits/stdc++.h>
# define int long long
# define ld long double
using namespace std;
const int MAXN=1e6+;
int N,P,L;
ld sum[MAXN],f[MAXN];
int last[MAXN],nxt[MAXN];
char s[MAXN][];
struct node{ int j,l,r;};
deque<node>q;
void Print_B()
{
puts("Too hard to arrange");
}
void Print_E()
{
for (int i=;i<=;i++) putchar();
putchar('\n');
}
void write(int x)
{
if (x<) { x=-x; putchar('-');}
if (x>) write(x/);
putchar(''+x%);
}
void writeln(int x)
{
write(x);putchar('\n');
}
inline int read()
{
int X=,w=; char c=;
while (!(c>=''&&(c<=''))) w|=c=='-',c=getchar();
while ((c>=''&&(c<=''))) X=(X<<)+(X<<)+(c^),c=getchar();
return w?-X:X;
}
ld pow(ld x,int n)
{
ld ans=;
while (n) {
if (n&) ans=ans*x;
x=x*x;
n>>=;
}
return ans;
}
ld calc(int i,int j)
{
return (ld)f[j]+pow(abs(sum[i]-sum[j]+(i-j-)-L),P);
}
void Clear()
{
memset(f,,sizeof(f));
deque<node>tmp; swap(q,tmp);
memset(last,,sizeof(last));
memset(nxt,,sizeof(nxt));
sum[]=;
}
signed main()
{
int T; T=read();
while (T--) {
Clear();
N=read();L=read();P=read();
for (int i=;i<=N;i++) {
cin>>s[i];
int len=strlen(s[i]);
sum[i]=sum[i-]+(ld) strlen(s[i]);
}
q.push_back((node){,,N});
for (int i=;i<=N;i++) {
while (!q.empty()) {
if (q.front().r<i) q.pop_front();
else {
q.front().l=i; break;
}
} int j=q.front().j;
last[i]=j;
f[i]=calc(i,j);
int pos=-;
while (!q.empty()) {
int lt=q.back().l;
int rt=q.back().r;
int jt=q.back().j;
if (calc(lt,i)<=calc(lt,jt)) {
pos=lt; q.pop_back(); continue;
} else
if (calc(rt,jt)<=calc(rt,i)) break;
else {
int l=lt,r=rt,ans=;
while (l<r) {
int mid=(l+r)>>;
if (calc(mid,i)<=calc(mid,jt)) r=mid;
else l=mid+;
}
q.back().r=l-; pos=l; break;
}
}
if (pos!=-) q.push_back((node){i,pos,N});
}
if (f[N]>(1e18)*1ll) Print_B();
else {
printf("%lld\n",(int)(f[N]+0.5));
for (int i=N;i;i=last[i]) nxt[last[i]]=i;
int now=;
for (int i=;i<=N;i++) {
now=nxt[now];
int tmp=now;
for (int j=i;j<tmp;j++) printf("%s ",s[j]);
puts(s[tmp]);
i=tmp;
}
}
Print_E();
}
return ;
}

 二维区间DP 四边形不等式定理:

 (特别的要求F[i][i]=w[i][i]=0)

如果有下面条件成立:

  1. w为凸
  2. 对于任意的a<=b<-c<=d有w(a,d)>=w(b,c)

那么F也为凸。

由于我们定义二元函数的凸性是有两种定义方法,

我们就是要证明:对于任意 i< i+1<=j< j+1,满足f[i][j]+f[i+1][j+1]<=f[i][j+1]+f[i+1][j](交叉小于等于包含)

设f[i+1][j]取最小值的时候k=x,f[i][j+1]取最小值的时候k=y

f[i][j]=f[i][x]+f[x+1][j]+w(i,j)

f[i+1][j+1]=f[i+1][y]+f[y+1][j+1]+w(i+1,j+1)

所以左式取最值的时候,左式=f[i][x]+f[x+1][j]+w(i,j)+f[i+1][y]+f[y+1][j+1]+w(i+1,j+1)

由于w为凸,所以w(i,j)+w(i+1,j+1)<=w(i+1,j)+w(i,j+1)

f[i][x]+f[x+1][j]+w(i,j)+f[i+1][y]+f[y+1][j+1]+w(i+1,j+1)<=f[i][j+1]+f[i+1][j]

右式=f[i][y]+f[y+1][j+1]+w(i,j+1)+ f[i+1][x]+f[x+1][j]+w(i+1,j)

得:

f[i][x]+f[x+1][j]+w(i,j)+f[i+1][y]+f[y+1][j+1]+w(i+1,j+1)<=f[i][y]+f[y+1][j+1]+w(i,j+1)+ f[i+1][x]+f[x+1][j]+w(i+1,j)

展开得:

f[i][j]+f[i+1][j+1]<=f[i][j+1]+f[i+1][j]

证毕。

 二维区间DP决策单调性定理:

如果  (特别的要求F[i][i]=w[i][i]=0)为凸

那么对于任意i<j都有P[i][j-1]<P[i][j]<P[i+1][j]

记p=P[i][j],对于任意的i<k<=p,由于F为凸那么f[i][p]+f[i+1][k]>=f[i][k]+f[i+1][p]

移项可得:f[i+1][k]-f[i+1][p]>=f[i][k]-f[i][p]

由于p最优,得f[i][k]+f[k+1][j]>=f[i][p]+f[p+1][j]

(f[i+1][k]+f[k+1][j]+w(i+1,j))-(f[i+1][p]+f[p+1][j]+w(i+1,j))

= f[i+1][k]-f[i+1][p]+f[k+1][j]-f[p+1][j]

>=f[i][k]-f[i][p]+f[k+1][j]-f[p+1][j]

= f[i][k]+f[k+1][j]-(f[i][p]+f[p+1][j])>=0

所以对于f[i+1][j],p比任何k<=p优所以P[i+1][j]>=P[i][j]

同理可知P[i][j-1]<=P[i][j]

四边形不等式优化定理总结

1.四边形不等式的定义:相交小于包含
两种等价定义:
对于a<=b<=c<=d属于Z,都有w(a,d)+w(b,c)>=w(a,c)+w(b,d)
对于a,b属于Z 若 a<b,有w(a,b+1)+w(a+1,b)>=w(a,b)+w(a+1,b+1)
2.一维线性DP决策单调定理
对于形如f[i]=min_{0<=j<i}{F[j]+w(j,i)}若w为凸那么F决策单调递增
3.二维区间DP决策单调性定理
对于形如F[i][j]=min_{i<=k<=j}{f[i][k]+f[k+1][j]+w(i,j)}
(特别的要求F[i][i]=w(i,i)=0) 若w为凸则F为凸,
对于F理应满足决策P[i][j-1]<P[i][j]<P[i+1][j]
P[l][r]表示当[l,r]分为[l,k]和[k+1,r]两部分时F[l][r]最大

利用第二种等价定义,证明函数w(x,y)的凸性事实上只要
证明对于任意j<i,w(j,i+1)+w(j+1,i)>=w(j,i)+w(j+1,i+1)
只需证明:w(j+1,i)-w(j+1,i+1)>=w(j,i)-w(j,i+1)
代入换元函数单调性可知w(x,y)的凸性

更方便的方案:打表暴力DP验证决策单调!

石子合并弱化版本 https://www.luogu.org/problemnew/show/U58387
石子合并强化版本 GarsiaWachs算法(这里放过了GW的暴力O(n^2)那是因为数据随机)

专题5:习题课

动态规划DP的优化的更多相关文章

  1. DP的优化总结

    一.预备知识 \(tD/eD\) 问题:状态 t 维,决策 e 维.时间复杂度\(O(n^{e+t})\). 四边形不等式: 称代价函数 w 满足凸四边形不等式,当:\(w(a,c)+w(b,d)\l ...

  2. 【BZOJ1150】数据备份(动态规划,凸优化)

    [BZOJ1150]数据备份(动态规划,凸优化) 题面 BZOJ 洛谷 题解 在不考虑\(K\)的情况下很容易\(dp\) 如果把\(K\)考虑进状态显然是\(O(n^2)\)级别. 所以凸优化一下即 ...

  3. 【BZOJ5311/CF321E】贞鱼/Ciel and Gondolas(动态规划,凸优化,决策单调性)

    [BZOJ5311/CF321E]贞鱼/Ciel and Gondolas(动态规划,凸优化,决策单调性) 题面 BZOJ CF 洛谷 辣鸡BZOJ卡常数!!!!!! 辣鸡BZOJ卡常数!!!!!! ...

  4. 【BZOJ5252】林克卡特树(动态规划,凸优化)

    [BZOJ5252]林克卡特树(动态规划,凸优化) 题面 BZOJ(交不了) 洛谷 题解 这个东西显然是随着断开的越来越多,收益增长速度渐渐放慢. 所以可以凸优化. 考虑一个和\(k\)相关的\(dp ...

  5. 【CF739E】Gosha is hunting(动态规划,凸优化)

    [CF739E]Gosha is hunting(动态规划,凸优化) 题面 洛谷 CF 题解 一个\(O(n^3)\)的\(dp\)很容易写出来. 我们设\(f[i][a][b]\)表示前\(i\)个 ...

  6. 【BZOJ3437】小P的牧场(动态规划,斜率优化)

    [BZOJ3437]小P的牧场(动态规划,斜率优化) 题面 BZOJ 题解 考虑暴力\(dp\),设\(f[i]\)表示强制在\(i\)处建立控制站的并控制\([1..i]\)的最小代价. 很显然,枚 ...

  7. 【BZOJ4654】【NOI2016】国王饮水记(动态规划,斜率优化)

    [BZOJ4654][NOI2016]国王饮水记(动态规划,斜率优化) 题面 BZOJ 洛谷 题解 首先肯定是找性质. 明确一点,比\(h_1\)小的没有任何意义. 所以我们按照\(h\)排序,那么\ ...

  8. 【BZOJ】1096: [ZJOI2007]仓库建设(dp+斜率优化)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1096 首先得到dp方程(我竟然自己都每推出了QAQ)$$d[i]=min\{d[j]+cost(j+ ...

  9. 【Wannafly挑战赛29F】最后之作(Trie树,动态规划,斜率优化)

    [Wannafly挑战赛29F]最后之作(Trie树,动态规划,斜率优化) 题面 牛客 题解 首先考虑怎么计算\([l,r]\)这个子串的不同的串的个数. 如果\(l=1\),我们构建\(Trie\) ...

随机推荐

  1. Linux 内核开发 - 内核定时器

    时间差的度量 系统的定时器硬件以固定的频率产生时钟中断,产生始终中断的间隔以HZ 常量来决定,通常在50~1200之间,x86默认是1000.HZ能够依据不同的内核来配置. Linux 採用jiffi ...

  2. 一文让您全面了解清楚HBase数据库的所有知识点,值得收藏!

    一.HBase基本概念:列式数据库 在Hadoop生态体系结构中,HBase位于HDFS(Hadoop分布式文件系统)的上一层,不依赖于MapReduce,那么如果没有HBase这种Nosql数据库会 ...

  3. 2.1《想成为黑客,不知道这些命令行可不行》(Learn Enough Command Line to Be Dangerous)——重定向文件和添加文件

    回忆第一章节的内容,我们用echo命令输出莎士比亚的第一首十四行诗的第一行(Listing 6): $ echo "From fairest creatures we desire incr ...

  4. 64位RHEL5系统上运行yum出现"This system is not registered with RHN”的解决方法

    在红帽EL5上运行yum,提示“This system is not registered with RHN”,意思是没有在官网上注册,不能下载RH的软件包,替代方案是采用centos源. 1.卸载r ...

  5. 20155330 《网络攻防》 Exp3 免杀原理与实践

    20155330 <网络攻防> Exp3 免杀原理与实践 基础问题回答 杀软是如何检测出恶意代码的? 基于特征码.先对流行代码特征的提取,然后进行程序的比对,如果也检测到相应的特征码的程序 ...

  6. 如何取得Oracle并行执行的trace

    如何取得Oracle并行执行的trace: ALTER SESSION SET tracefile_identifier='10046_PROD';ALTER SESSION SET max_dump ...

  7. 洛咕 P3756 [CQOI2017]老C的方块

    四染色,贼好想 一个弃疗图形刚好对应一个红-绿-黄-粉色路线(不要吐槽颜色) 就是裸的最小割,建图傻逼懒得写了 #include<bits/stdc++.h> #define il inl ...

  8. centos 7 git的管理和使用

    一.linux 安装git (服务端) 1.首先创建用户账号 useradd zlx passwd zlx .... 2.创建目录git仓库 mkdir zlx_git.git 3.赋权限 chown ...

  9. GitHub 新手教程 一,GitHub 注册

    1,注册地址: https://github.com/ 2,输入账号.邮箱.密码: 3,选择 Free 免费账号: 4,选择一些基本信息(翻译后中文见下面的图): 翻译如下: 5,打开你注册用的邮箱, ...

  10. cocos2dx渲染架构

    2dx的时代UI树便利和渲染是没有分开的,遍历UI树的时候就渲染.3dx版本为了分离了ui树的遍历和渲染,先遍历生成渲染命令发到渲染队列,之后遍历渲染命令队列开始渲染.这样做的好处是渲染命令可以重用, ...