zoukankan      html  css  js  c++  java
  • [正睿集训2021] dp优化

    四边形不等式

    现在我们有一个 (dp) 方程形如:(f(i)=min{f(j)+w(j,i)})

    决策单调性的定义是对于 (a<b<c<d),若 (f(c)) 时从 (b) 转移比 (a) 转移优,那么对于 (f(d)) 也是从 (b) 转移比从 (a) 转移更优,即决策点位置单调。

    如果 (w) 满足四边形不等式,那么 (w(a,c)+w(b,d)leq w(a,d)+w(b,c)),如果四边形不等式成立,那么如果 (f(a)+w(a,c)geq f(b)+w(b,c)),就有 (f(a)+w(a,d)geq f(b)+w(b,d)),这就是决策单调性的数学语言。

    上面的东西怎么证明呢?很简单,你把四边形不等式乘 (-1) 然后不等式合并。

    只要我们证明四边形不等式就可以用决策单调性了,也就是说四边形不等式是决策单调性的充分条件,但是你推不出来怎么办呢?其实打表是一种好方法,四边形不等式还有下面一种等价形式:

    [w(j,i+1)+w(j+1,i)geq w(j,i)+w(j+1,i+1) ]

    具体应用可以看下面例题的 [NOI2009]诗人小G

    max卷积

    ( t max) 卷积就是形如下面式子:

    [c_n=max{a_i+b_{n-i}} ]

    一般的 ( t max) 卷积是无法优化的,只能 (O(n^2)) 做。但如果 (a,b) 都是凸函数,那么可以直接 (O(n)) 合并,可以用 ( t two-pointers) 来维护,每次就贪心地选增量最大的就行了。

    如果 (a) 是凸函数 (b) 没有特殊限制,那么可以用单调性优化 (dp) 来做,时间复杂度 (O(nlog n))

    这个东西暂时还没有例题

    篮球

    题目描述

    (n) 种物品,第 (i) 种物品有 (c_i) 个,初始代价是 (a_i),被选一次代价就会减少 (b_i)(第 (k) 次选的代价就是 (a_i-(k-1)b_i)),你想知道对于 (k=1...m),总共选 (k) 件物品代价的最小值是多少。

    (nleq 1000,mleq10000)

    解法

    首先不难发现一个结论:至多有一种物品是部分选,其他物品要么全选或者全不选

    考虑暴力 (dp),枚举这个不被全选的是谁,然后前缀后缀分别做背包,算出恰好 (k) 次的最小代价,最后再和这个不被全选的合并。但是暴力的复杂度是 (O(nm^2))

    考虑分治优化,设 (dp(l,r)) 表示计算出了一个 (dp) 数组,且这个不被全选在 ([l,r]) 中的答案。那么每次取 (mid),递归左半边就可以算出 (dp(l,mid)),然后把右半边的一个一个加进去就行,右半边同理。然后两边算出来的 (dp) 数组取 ( t min) 就可以了。

    每一层都是一个 (01) 背包,总复杂度 (O(nmlog n))

    序列

    题目描述

    (n) 种物品,第 (i) 种物品第一次选择的收益是 (a_i),之后每次选择的收益都是 (b_i),代价始终为 (c_i),你需要求出总代价不超过 (m) 下收益的最大值。有 (q) 次修改,第 (j) 次修改会在第 (i) 次修改的基础上修改一个物品的两类收益,对于每次修改都要输出答案。

    (n,m,Qleq 2000)

    解法

    先考虑没有修改怎么做,设 (f(i,j)) 为前 (i) 种物品选了 (j) 代价的最大收益,转移就先强制选一个 (a_i),然后选 (b_i) 的就是完全背包了。还有一种情况就是不操作,直接和 (f(i-1,j)) 取最大值。

    考虑如何修改,修改一定是一个树形结构。对于每个物品我们先单独考虑,因为修改相当于是改一个子树内的 (a_i,b_i),也就是改一段 (dfs) 序区间的 (a_i,b_i),那么我们把这种修改离线下来,就能得到 (dfs) 序列上若干段区间 (a_i,b_i) 都是相同的。

    考虑线段树分治,那么就只有加入操作了,由于加入操作最多打在线段树上 (O((n+q)log n)) 次,然后一次加入操作的时间是 (O(m)) 的,所以总时间复杂度 (O((n+q)mlog n))

    [HDU 6800] Play osu! on Your Tablet

    题目描述

    一个二维点序列 ((a_i,b_i)),你需要把它拆成两个子序列,使得两个子序列相邻两项的 曼哈顿距离 最小。

    (nleq 1e5)

    解法

    首先不难想到一个暴力 (dp),设 (f(i,j)) 为考虑到第 (i) 个,其中一个子序列的结尾是 (i),另一个子序列的结尾是 (j) 的最小代价,转移就从 (f(i-1,...)) 而来:

    • 如果 (j<i-1),那么 (f(i,j)=f(i-1,j)+dis(i-1,i)),就只有这一种转移方式。

    • ( t otherwise)(f(i,i-1)=f(i-1,k)+dis(k,i))

    第一个式子是整体平移较为简单,我们想对第二个式子下手。第一种思路是:(dp) 式继续展开看有没有什么特殊性质 。所以我们看看展开 (f(i-1,k)) 后会是什么样子:

    [f(i,i-1)=maxegin{cases}f(i-1,i-2)+dis(i-2,i)&k=i-2\f(i-2,k)+dis(i-2,i-1)+dis(k,i)&k<i-2end{cases} ]

    如果我们将 (f(i-2,k)) 继续展开,那么得到的也是类似的结果,所以转移可以写成:

    [f(i,i-1)=max_{k=1}^{i-1}{f(k,k-1)+sum_{j=k}^{i-2}dis(j,j+1)+dis(k-1,i)} ]

    (g(i)=f(i,i-1)),那么 (g) 的转移就只和自己有关系了,且我们可以建一个虚点 (n+1),所以不难发现最后的答案也是很容易用 (g) 来算的,那么只需要考虑 (g) 的转移就行了,设 (s_i=sum_{i=1}^{i-1}dis(i,i+1)),那么重新写一下转移方程:

    [g(i)=max_{k=1}^{i-1}{g(k)-s_{k}+dis(k-1,i)}+s_{i-1} ]

    下面介绍另一种思路:高维 (dp) 转低维 ,考虑枚举一段后缀是同一个子序列,也能推出上面的式子。

    现在就来优化 (g(i)) 的转移吧,考虑只有 (dis(k-1,i)) 这个东西难做一点,写成曼哈顿距离实际上是 (|x_{k-1}-x_i|+|y_{k-1}-y_i|),所以可以分四种情况讨论,拆分成之和 (k) 有关的式子和之和 (i) 有关的式子即可。然后就是一个三维偏序问题用 ( t cdq) 分治即可解决(还有一维偏序关系是下标)

    [HDU 6566] The Hanged Man

    题目描述

    (n) 个点的树,每个点有两种权值 (a_i,b_i),你需要选出一个独立集(里面的点两两不相邻),使得 (a_i) 的总和恰好为 (m),且 (b_i) 的总和尽量大,同时你需要计算这样独立集的数量。

    (nleq100,mleq5000)

    解法

    暴力 (dp)(O(nm^2)),多说无益,我们要让复杂度往 (n) 那边倾斜。

    正解是 点分树优化dp ,先建出原图的点分树,我们主要想利用的性质是:点分树的深度不超过 (O(log n)),并且和一个点相邻的点要么是它点分树上的祖先,要么是它子树内的节点。

    那么我们就按 (dfs) 序来一个个加入每个点,就考虑一个点选还是不选来转移,但是为了满足独立集这个条件,我们暴力记录下一个点祖先的状态,只要保证如果选出来的话它和它的祖先不相邻即可。

    转移需要讨论一下这个点和上一个点的位置关系,这个自己 (yy) 一下就好啦,时间复杂度 (O(n^2m))

    小D与随机

    题目描述

    大爱 (wzy) 鸽鸽,这讲得也太好了吧!

    有一棵 (n) 个节点的有根树,随机生成一个排列 (p) 表示树上每个点的权值。定义一个点为好点当且仅当这个点的权值比所有祖先的权值大,求 (k) 的好点个数次方的期望。乘以 (n!) 后对 (998244353) 取模。

    (nleq 5000)

    解法

    看到这种树上大小关系的题一定要想:例四,就是 (dp) 维护容斥系数。

    把好点涂黑,剩下的叫白点,那么把限制用大小转移表示

    • 白点要比第一个黑点祖先小
    • 黑点要比第一个黑点祖先大

    我们从大点连向小点,并称这样的点为正向边,设 (f(i,j)) 表示子树 (i) 里面有 (j) 的点连到上面去了(能通过外向树走到上面,方便统计大小),先求出所有儿子的 (f) 卷积 (g(j))(这里期望可以直接相乘),接下来考虑:

    • 如果 (i) 是黑点,那么 (i) 一定是 (j) 个点中最小的,这对应了一个概率 (frac{1}{j+1}),那么这么转移 (f(i,j+1)+=g(j) imesfrac{k}{j+1})
    • 如果 (i) 是白点,那么就容斥这条边,如果断开那么 (f(i,j)+=g(j)),如果反向那么 (f(i,j+1)-=g(j)),因为子树大小为 (1) 所以就不用除啦。

    根据原理:两个点只在他们的 (lca) 处对复杂度有 (1) 的贡献,那么总复杂度 (O(n^2))

    [NOI2009] 诗人小G

    题目描述

    点此看题

    解法

    (sum(i)=i+sum_{j=1}^ia_j),那么有很显然的 (dp) 方程:

    [f(i)=min{f(j)+|sum(i)-sum(j)-1-L|^p} ]

    现在来证明上面的转移方程式决策单调的,那么就可以用单调队列优化了。

    (w(j,i)=|sum(i)-sum(j)-1-L|^p),我们只需要证明这个不等式:

    [w(j,i+1)+w(j+1,i)geq w(j,i)+w(j+1,i+1) ]

    (u=sum(j)-sum(j)-1-L,v=sum(i)-sum(j+1)-1-L),那么就是要证明这个东西:

    [|u+1+a_{i+1}|^p+|v|^pgeq |u|^p+|v+1+a_{i+1}|^p ]

    [|v|^p-|v+1+a_{i+1}|^pgeq|u|^p-|u+1+a_{i+1}|^p ]

    所以我们只需要证明函数 (h(x)=|x|^p-|x+z|^p(zin[0,infty))) 单调不增,那么等价于它的导数小于 (0),我们分类讨论一下。

    (xin[0,infty)) 时:

    [h(x)=x^p-(x+z)^p ]

    [h'(x)=px^{p-1}-p(x+z)^{p-1} ]

    不难发现后面的一定更大,所以 (h'(x)leq 0)

    (xin(-infty,0]) 时,且 (p) 是偶数:

    (h(x)=x^p-(x+z)^p)

    和第一种情况同理,直接求导即可。

    (xin(-infty,-z)),且 (p) 是奇数:

    [h(x)=-x^p+(x+z)^p ]

    [h'(x)=-px^{p-1}+p(x+z)^{p-1}leq 0 ]

    (xin[-z,0)),且 (p) 是奇数:

    [h(x)=-x^p-(x+z)^p ]

    [h'(x)=-px^{p-1}-p(x+z)^{p-1}leq 0 ]

    那么所有情况我们都讨论完啦!证毕


    现在来讲一讲怎么写决策单调性的题,主要是要利用决策点 (p_{i-1}leq p_i) 的条件,那么每个点一定对应了一段区间,它作为这一段区间的决策点。

    所以我们维护一个决策点的单调队列,假设现在要处理 (i) 的转移,我们想要让队首就是 (i) 的最优决策点,可以一个二分函数计算一个决策点最远到哪里比另一个决策点优,就很容易判断第二个点能不能弹出队首了。

    然后考虑怎么插入,还是用最远管辖点来判断,如果队尾在还没有起作用的时候就已经比 (i) 劣了,那么队尾就可以直接扔掉了,总时间复杂度 (O(nlog n))

    CF868F

    题目描述

    点此看题

    解法

    首先写出暴力 (dp) 方程,设 (f(i,j)) 表示前 (i) 个位置划分成 (j) 段的最小费用,转移:

    [f(i,j)=min{f(k,j-1)+w(k+1,i))} ]

    然后不难发现 (w) 满足四边形不等式,因为 (C(x,2)) 增长得越来越快:

    [w(j,i+1)+w(j+1,i)geq w(j,i)+w(j+1,i+1) ]

    但是好像套诗人小G的做法不行,因为这道题的 (w) 不能 (O(1)) 算。这里讲一种崭新的做法,考虑分治,设函数 (solve(l,r,L,R)) 表示区间 ([l,r])([L,R]) 转移而来,每次我们找到 (mid) 的转移点 (p),分成 (solve(l,mid-1,L,p))(solve(mid+1,r,p,R)) 递归下去。

    那么 (w) 怎么算呢?用类似莫队的操作维护即可,时间复杂度为什么是对的呢?因为考虑一个函数的功能完成之后只需要花 (O(r-l)) 的时间就可以跳到兄弟函数,或者是返回上一层函数。那么总时间复杂度 (O(nlog n))

    [八省联考2018] 林克卡特树

    题目描述

    点此看题

    解法

    这个题怎么证凸函数啊?还有这个题意转化也是神奇得一匹

    事实上就是选 (k+1)点不交的链使得经过的边权和最大。

    (wqs) 二分之后需要考虑链任意选,但是选一条链需要花费 (mid) 的代价怎么做。

    (f(i,0/1/2)) 表示点 (i) 下面有 (0/1/2) 条边在链上。转移由于要记录下用了多少链所以我们把 (dp) 数组开成一个 (pair) 会好写很多,(f(i,0)) 回溯的时候可以存最大值,第二个细节是需要尽可能多的选取链,单点也算一条链。

    于是总复杂度 (O(nlog n))我竟然写了代码

    #include <cstdio>
    #include <cstring>
    const int M = 300005;
    #define int long long
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,k,up,mid,ans,tot,f[M];
    struct edge
    {
    	int v,c,next;
    }e[2*M];
    struct node
    {
    	int x,y;
    	node(int X=0,int Y=0) : x(X) , y(Y) {}
    	node operator + (int r) {return node(x+r,y);}
    	node operator + (node r) {return node(x+r.x,y+r.y);}
    	bool operator < (node r) {return x==r.x?y<r.y:x<r.x;}
    }dp[M][3];
    int Abs(int x)
    {
    	return x>0?x:-x;
    }
    node max(node a,node b)
    {
    	return a<b?b:a;
    }
    node upd(node a)//新增一条链 
    {
    	return node(a.x-mid,a.y+1);
    }
    void dfs(int u,int fa)
    {
    	dp[u][2]=max(dp[u][2],upd(dp[u][2]));
    	for(int i=f[u];i;i=e[i].next)
    	{
    		int v=e[i].v,c=e[i].c;
    		if(v==fa) continue;
    		dfs(v,u);
    		dp[u][2]=max(dp[u][2]+dp[v][0],upd(dp[u][1]+dp[v][1]+c));
    		dp[u][1]=max(dp[u][1]+dp[v][0],dp[u][0]+dp[v][1]+c);
    		dp[u][0]=dp[u][0]+dp[v][0];
    	}
    	dp[u][0]=max(dp[u][0],max(upd(dp[u][1]),dp[u][2]));
    }
    signed main()
    {
    	n=read();k=read()+1;
    	for(int i=1;i<n;i++)
    	{
    		int u=read(),v=read(),c=read();
    		up+=Abs(c);
    		e[++tot]=edge{v,c,f[u]},f[u]=tot;
    		e[++tot]=edge{u,c,f[v]},f[v]=tot;
    	}
    	int l=-up,r=up;
    	while(l<=r)
    	{
    		mid=(l+r)>>1;
    		for(int i=1;i<=n;i++)
    			dp[i][0]=dp[i][1]=dp[i][2]=node(0,0);
    		dfs(1,0);
    		if(dp[1][0].y>=k)
    		{
    			ans=dp[1][0].x+mid*k;
    			l=mid+1;
    		}
    		else r=mid-1;
    	}
    	printf("%lld
    ",ans);
    }
    

    yet another ioi problem

    题目描述

    一条链长度为 (L),有 (n) 个位置上有村庄,你需要选定恰好 (k) 个位置摆放水井,使得这 (n) 个村庄到最近水井的距离和最短。

    (1leq n,kleq 1e5,Lleq 1e9)

    解法

    又要做题意转化了,因为要判断哪个水井是最近的有点麻烦。我们考虑如果只有一个水井那么一定是放在中位数的位置,我们不妨转化成把原序列划分成 (k) 段,每一段的水井都放在中位数的地方并且强制走到这个水井,由于可以用 (dp) 解决这个最优化问题所以这个题意转化是对的。

    看到 恰好k个位置 这类表述一定要想到 (wqs) 二分。可以感知一下距离和是随 (k) 单调递减的,而且减少得越来越慢,所以可以去掉恰好 (k) 段这个限制条件。设 (f(i)) 表示划分到 (i) 的最小代价:

    [dp(i)=min{dp(j)+w(j+1,i)-k} ]

    然后考虑下面的式子显然成立,所以直接用决策单调性即可:

    [w(j,i+1)+w(j+1,i)geq w(j,i)+w(j+1,i+1) ]

  • 相关阅读:
    最短路径问题/Spfa
    cddiv/数组维护
    cfdiv2/c/找规律
    Codeforces Round #343 (Div. 2)【A,B水题】
    POJ 2135_Farm Tour
    POJ 3469_Dual Core CPU
    POJ 3469_Dual Core CPU
    POJ 3281_Dining
    POJ 3281_Dining
    POJ 3041_Asteroids
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/14433290.html
Copyright © 2011-2022 走看看