一些可行解问题
网格问题
L形覆盖
TODO
原序列恢复问题
从每对元素和恢复
题目1:给定n个元素a1,…,an,以及它们的m=(n2)个配对的和组成的序列b1,b2,…,bm。现在已知序列b,求一组可能的a,如果不存在则报告无解。
提供一道题目。
首先当n≤2的时候,问题是非常简单的,这里仅考虑n≥3的情况。
首先我们将b进行排序,并且认为a也是有序的。很显然这时候a1+a2=b1,a1+a3=b2,之后我们可以发现a2+a3可能是b3,b4,…,bn中的任意一个。我们暴力枚举a2+a3=bi,之后我们可以通过a1=b1+b2−bi2来得到一个可能的a1,最多有n−2个可能的选择。
接下来我们来考虑已知a1,构造其它元素的算法。
我们扫描b,假设现在在扫描bi,并且我们已经求解了a序列的前k个元素,并且已知元素的配对和的集合为S,且S中存放一个额外的哨兵−∞。这时候有三种情况:
- bi>min(S),这时候一定无解。
- bi=min(S),这时候我们从S中删除bi并结束流程。
- bi<min(S),我们得到ak+1=bi−a1,并且将a2+ak+1,a3+ak+1,…,ak+ak+1加入到S中。
上面的流程可以通过维护一个优先队列实现,总的时间复杂度为O(n2log2n2)。
由于这个流程我们总共会调用O(n)次,因此总的时间复杂度为O(n3log2n2)。
从子集和恢复
给定一个包含n个整数的多重集合A,以及它的2n个子集和S。现在我们丢失了A,仅保留子集和,希望找到一个合法的集合A,或者判断无解。其中1≤n≤20,且子集和为整数(可能为正数或负数)。
一道原题。
题解来自这里。我这里重新阐述一下。
w(T)表示T中所有元素的和。
首先我们考虑A中仅存在非负整数的情况。我们从S中删除一个0,之后S中仅包含所有非空集合的子集和。我们可以贪心求解剩下的问题,维护集合A,初始为空。设S中最小的元素为e,我们将e加入A中,并从S中删除所有包含e的A的子集和。重复上面这个流程直到S为空。最后只要验证A的大小正好为n即可。
下面我们考虑扩展原问题,支持子集和中出现负数。
设最小的负数为m。之后我们考虑S−m(即将S中所有元素都减去m),找到它的一组可行解B。之后我们考虑B中的一个和为−m的子集B−1。我们取A为B中B−1部分所有元素取反后得到的集合。考虑任意B的子集T,可以发现∑T⊕B−1∈S,其中T⊕B−1表示对于任意e∈T,如果e∈T,则从T中删除e,否则将−e加入T中,很显然同时有T⊕B−1⊆A。可以发现⊕操作是可逆的,它是一个定义在B的子集族,值域为A的子集族的双射函数。
同理如果A存在,我们可以记A−1表示A中所有负数的集合。考虑任意A中的集合T,我们可以通过将A中A−1部分全部取反得到B,可以发现B是S−m的一个有效解。实际上对于任意T⊆A,可以发现T⊕A−1⊆B。且⊕操作是可逆的,它是一个定义在A的子集族,值域为B的子集族的双射函数。
因此我们给出了原问题解和S−m问题解的转换方式,以及二者解存在的等价性。
图问题
哈密顿环分解
二分图版本
题目1:给定二分图G=(V,E),其中V由两个点集A和B组成,且所有边的一个端点为L中顶点,另外一个端点为R中顶点。要求将二分图分为任意个哈密顿环,要求每个顶点恰好在一个哈密顿环中出现正好一次,其中1≤V,E≤105。
提供一道题目:SRM788 ParkPlace。
实际上哈密顿回路问题一般需要转换为欧拉环才能做。我们要做的实际上是删除一些边,保证每个顶点的度数都是2,这时候跑出的欧拉环实际上就是哈密顿路径。
要让每个顶点的度数都为2,我们可以考虑加入一个超级源点和汇点,之后从源点向所有L中顶点拉一条容量为2的边,从R中顶点向汇点拉一条容量为2的边。这样如果能跑出最大流2|L|=2|R|,就能得到哈密顿环分解。
有向图版本
题目1:给定有向图G=(V,E)。要求将图分为任意个哈密顿环,要求将图分解为若干个哈密顿环,每个顶点恰好在一个哈密顿环中出现正好一次,其中1≤V,E≤105。
由于是哈密顿回路,因此我们实际上需要删除一些边,保证每个顶点的入度和出度均为1,这样我们就能将图分解为若干个欧拉环。而在度数为1的时候,欧拉环实际上就是哈密顿回路。
接下来的问题就是我们需要删除一些边,来保证每个顶点的入度和出度都是1。一种简单的思路是我们将每个顶点分解为两个顶点,一个染黑色,一个染白色,对于有向边(u,v),我们从黑u向白v拉一条边。这样如果我们能找到一个完美匹配,就能找到一个哈密顿回路了。
时间复杂度为O(E√V)。
哈密顿路径
题目1:给定一个包含n个顶点的完全图,之后给定一个序列a1,…,an,其中ai≠i,要求从图中删除边(i,ai)。要求找到图中的一条哈密顿路径,或者报告无解。其中2≤n≤106。
提供一道题目。
很显然n=2的时候一定无解。我们证明其它情况下一定有解。
我们可以认为算法的流程如下,初始的时候我们有一个禁用元素x=−1和一个空序列b,之后我们要选择一个未在b出现的数y,且y≠x,加入到b中,之后令x=ay。
下面我们证明这样一个命题:当n≥3的时候,且x,a1,…,an中没有任意一个数出现达到n次,那么一定有解。
当n=3的时候我们可以暴力枚举所有可能的置换,并证明解都是存在的。下面我们认为在较小的n的时候命题都成立,那么我们可以随机选择一个数i,如果ai在a1,…,an中出现次数达到n−1,那么这意味着x≠ai,我们将ai加入到b尾部,这时候可以发现还剩下n−1≥2个元素,它们是一个团,我们可以挑选一个合适的元素加到b尾部,之后可以随意插入。否则这时候a1,…,ai−1,ai+1,…,an依旧满足条件,通过归纳法可以证明解的存在。
因此这里我们就给出了解法,每次随机选择一个数i,之后检查ai是否被剩下的顶点所孤立,如果是则使用ai即可。直到顶点数剩下3个的时候,尝试所有置换即可。
时间复杂度为O(n+3!)。
环与最大公约数
考虑这样一个问题,给定n个顶点,m条带权边的一个有向图。
下面要求回答q个询问。每个询问要求判断是否存在这样一个环(环中可以出现重复的边),其中包含给定点vi,且环的长度L,满足L=xi(modpi)。
其中1≤n,m,q≤106,且边的权重为正数,且不超过109。
提供一道题目。
很显然,环中出现的点一定属于相同的双连通分量,因此我们可以单独考虑每个不同的双连通分量。下面我们假设整个图双连通。
我们从点1出发,维护每个点可能的距离,对于顶点i,其形式为kci+di,其中k≥0,初始的时候有ci=0,这表示对于任意模数p,图中都存在长度为ci的环,且从点1到点i存在一条长度为di的路径。
之后可以类似最短路一样进行迭代更新,这里可以仿照spfa算法。现在我们尝试要合并两个公式kc+d和te+g。考虑c和e的具体意义,这意味着图中存在长度为l=gcd(c,e)的环。这样两个公式可以化作kl+d和kl+g。接下来考虑d和e的意义,考虑任意一条长度为M的从当前顶点到顶点1的路径,那么这指出图中存在两个长度为d+M和g+M的环,因此一定存在长度为|d−g|的环(注意都是在对p取模意义下的)。因此合并后的结果为k⋅gcd(c,e,|d−g|)+d,很显然合并后的结果,包含kc+d和te+g的所有信息。
很显然一个顶点只会被更新最多O(log2109)次(每次都从初始的di中删除掉一些因子),因此预处理时间复杂度为O((n+m)log2109)。
但是实际上我们可以观察发现,我们要求的环的长度实际上只需要考虑所有的边即可,只需要预先通过递归算出di。这样做预处理的时间复杂度为O(m+log2109)。
接下来询问就非常容易了,我们只需要找到cvi,如果gcd(cvi,pi)∣xi,那么就存在,否则不存在。这部分时间复杂度为O(qlog2109)。
生成树
题目1:给定一个包含n个顶点m条边的无向连通图,且第i个顶点的资金为wi。现在要求找到一个边序列e1,…,en−1,其中这组边形成一个生成树,且边序列合法。一个序列合法是指在处理e1=(u,v)的时候,有u所在连通块的剩余资金与v所在连通块的剩余资金之和至少为x,之后合并二者所在的连通块,并且将剩余资金减去x,在这个情况下e2,…,en−1是合法的序列。判断是否存在这样的序列,如果存在则找到一个合法序列。
提供一道题目。
很容易序列存在的必要条件,发现一个必要条件∑ni=1wi≥(n−1)x。下面我们证明这同时也是充分条件。我们通过构造法来证明。
如果存在一个连通块剩余资金不少于x,那么就从它出发与相邻的其它连通块合并。我们重复上面这个过程,直到所有连通块的资金都少于x。
接下来考虑如果存在两个不同的连通块,它们的剩余资金额少于x,那么一定有∑ni=1wi<x+(n−2)x=(n−1)x,这与前提相悖,因此不可能发生。这意味着这时候任意两个相邻的连通块都可以合并,并且合并后的所有连通块依旧满足没有连通块的资金量达到x。
上面的过程可以O(n+m)解决。
元素替换
两元素和替换
题目1:初始的时候,你有两个整数1,0,之后你需要不断的计算两个整数的和,并用来替换其中一个整数。要求找到最少的操作次数,使得某个元素等于c。要求构建这样一组操作序列并输出。
我们可以枚举最后的状态(c,x),其中0≤x≤c。之后可以发现它的前一种状态是唯一的,只有(c−x,x)。因此根据这个唯一性,我们可以判断它是否是一个可行的结果。可以注意到这里类似于欧几里德算法的流程,其时间复杂度为O(log2c)。
因此总的时间复杂度为O(clog2c)。
题目2:初始的时候,你有两个向量(1,0)和(0,1)之后你需要不断的计算两个向量的和,并用来替换另外一个向量。要求构造出一个可能的操作序列,使得(x,y)出现在最终的两个向量中,或者报告无解。
这里如果我们采用题目1的方式,暴力枚举最终状态,时间复杂度为O(xylog2xy)。
但是可以发现这个实际上是The Stern-Brocot tree的构建过程。其有解的前提是gcd(x,y)=1。之后我们可以模拟SB树的构建流程,得到最终的序列。
动态规划
归0问题
题目1:给定2n个数,其中n个为正数,n个为负数,且所有数的绝对值不超过n。现在希望找到一个非空子集,满足子集和为0。
提供一道题目。
将正数集合记作A,将所有负数绝对值集合记作B。不失一般性,认为B的总和不小于A。记ai表示A中前i个元素的和,bj表示B中前j个元素的和。对于每个i,找到一个最大的fi,满足ai≥bfi,很显然此时0≤ci=ai−bfi≤n−1。如果c1,…,cn中存在0,则我们直接找到了一个解。否则根据鸽巢定理,至少存在一对不同的下标i,j,满足ci=cj且i<j,这意味着我们可以选择第i+1个正数到第j个正数,以及第fi+1个负数到第fj个负数,它们合成的子集和为0。
上面的算法的时间复杂度为O(n)。
题目2:给定n个数a1,…,an,每个数的范围都在−1000到1000范围内。要求找到一组非负系数c1,…,cn,满足∑ni=1ciai=0,且∑nici最小。其中n≤106。
一道题目。
先讲一下我的做法。首先如果有解,要么存在某个ai=0,要么存在两个数ai<0<aj。前者非常简单,所以我们仅讨论一下后者。这时候我们发现取ci取aj,而cj取−ai的时候,就有ciai+cjaj=0。因此我们可以直接推出结果不可能超过2000。
实际上我们可以将a序列去重,这样就能保证n≤2000+1。
我们可以用最短路来解决这个问题,初始点为a1,…,an,而最短路需要考虑的范围为−2⋅106到2⋅106之间。把每个数当成边,边长都是1,因此如果我们用BFS的话时间复杂度为O(8⋅109)。但是这里可以用BitSet优化,因此时间复杂度为O(86⋅108)。还是可以接受的。
但是这个问题实际上可以优化到O(20012)。我们可以认为取到的最短的总和为0的序列为b1,…,bk,由于前缀和和后缀和异号,因此我们能通过调整顺序保证所有前缀和的绝对值都不超过1000。于是我们就只需要为−1000到1000建立2001个顶点,跑最短路。
并且这里我们还可以继续采用BitSet进行优化,这样时间复杂度可以降低到O(2001260)。
数论
前缀和非0
题目1:给定n个数a1,…,an以及一个素数p,要求重新排列这n个数,满足新序列的任意一段非空前缀和在模p意义下都不等于0,或者报告无解。
提供一道题目。
首先如果∑ni=1ai=0(modp),那么肯定无解。否则我们可以将所有0放在尾部。现在我们假设序列中不含0。
记x为出现次数最多的元素,记C(x)表示x的出现次数,那么2⋅C(x)≤n的情况下一定是有解的。因为我们每次可以选择一个数加到构建的序列尾部,我们直接选x,如果不行,就选择任意一个可行的数y,之后再加入x,由于y≠0,因此次数加入x,前缀和不可能变成0。可以发现这样操作下去会使得出现次数最多的数始终不超过一半。(除非只剩下一个元素,这时候由于保证总和非0,因此直接追加就可以了)
但是实际上条件还要更宽松点。考虑将所有元素都乘上x−1,注意这样做不会影响前缀和是否等于0。这时候1的出现次数最多,我们最多可以允许多少个1,答案是p−1+∑p−1i=2(p−i)C(i)。做法就是先使用p−1个1,之后追加一个非1元素y,之后追加p−y个1。
要构建一个具体的解,我们可以用贪心算法来做,每次选择出现次数最多的元素,增加直到它不再最多或者会出现0前缀为止。时间复杂度为O(nlog2n)。
不同前缀和
题目1:将0,1,…,n−1重新排列,要求所有前缀和在模n意义下不同,如果无解则报告。
首先特判1,一定是有解的。
容易发现0只能出现在首尾。无论哪种情况,如果1+2+…+(n−1)=0(modn),那么一定无解。
之后考虑偶数的情况。实际上这种情况下一定有解,设a为答案序列,如果i是偶数,则ai=−i(modn),如果i是奇数,则ai=i。这时候前缀和为0,1,−1,2,−2,…,n2,两两不同。
不同前缀积
题目1:将0,1,…,n−1重新排列,要求所有前缀积在模n意义下不同,如果无解则报告。
首先特判1,一定是有解的。
容易发现0只能出现在尾部。如果n是合数,则仅4可能有解,否则一定无解。事实上如果n=xy,其中x≠y,那么任意同时包含x,y的前缀积一定为0。故只有可能n=p2且n/p−1=1的情况下可能有解,其中p是素数。这时候只有可能是4。
之后考虑素数的时候的情况。如果我们找到一个原根g,之后考虑前1,…,n−1的排列,这时候我们将所有元素x转换为loggx后,先在前缀积等价于gti,其手中ti是对数形式的前缀和。我们只需要保证前缀和不同即可。着回到了不同前缀和问题。
但是实际上还存在一种更加简单的方式,就是令第一个元素为1,而第i个元素为ii−1,i>1。这时候前i个元素的前缀积为i,且所有元素不同。
区间和问题
题目1:给定一个长度为n的数组a,数组中的初始值都是0。现在给定m个请求,第i个请求给定一个区间[li,ri]以及一个值vi,其表示将区间中所有元素都加上vi。现在要求找出一组可能的v1,…,vm,要求满足|vi|=1,且执行完所有请求后,数组中所有元素绝对值不超过1。
对于请求A,用A.l表示A的左边界,A.r表示A的右边界,A.v表示A的修改值。
我们维护所有未处理的请求的集合Q,记L(Q)=minA∈QAl。则我们执行下面流程,并且始终保证下面不变式:
对于i<L(Q),有|a1|≤1,而对于i≥L(Q),有ai=0。
如果剩下的请求不超过2个,那么我们任意赋值即可,这样就得到一组可行解。否则我们可以从Q中弹出左边界最小的两个请求(如果有多个则任意),分别记作A和B,其中我们用A.l表示A的左边界,A.r表示A的右边界。分多种情况讨论:
- A.r<B.l,我们可以任意赋值A.v,之后将B插回Q。
- A.r≥B.r。我们在A和B之间加入一条边,表示二者不能赋予相同值。之后将A.l修改为B.r+1,如果A代表的区间不空,则重新插回A。
- A.r<B.r。我们在A和B之间加入一条边,表示二者不能赋予相同值。之后将B.l修改为A.r+1,如果B代表的区间不空,则重新插回B。
可以发现上面过程中不变式始终是成立的。但是实际上还有一些请求我们尚未真正的赋值。我们可以发现如果将请求视作顶点,之后问题变成了二染色问题。对于边(u,v),且u比v更早删除,则认为u的父亲为v,可以发现图实际上是一个森林,里面是没有环的,因此始终是可以二染色的。
这里我们可以用一个最小堆来维护Q。时间复杂度为O(mlog2m)。
二维移动问题
题目1:在一个二维平面上,我们初始在(0,0),目的地为(x,y)。现在允许移动n次,第i次移动的距离为di。每次移动需要选择上下左右四个方向中的一个移动。现在问是否存在一种方向选择,使得我们最终能移动到(x,y),如果有则找到一种方案。其中1≤n≤105,1≤di≤103。
提供一道题目。
首先这个问题很难直接做。因为我们需要将移动分类,分别作用在横坐标或纵坐标上。但是很神奇的是,如果我将所有坐标都绕原点旋转45度,现在我们可以沿着四个对角线移动。(这里为了方便,我们可以把坐标再乘上√2以避免出现小数)。
这时候我们要移动到(x−y,x+y)去,对于第i次移动,可选的移动为(\plusmndi,\plusmndi),这意味着横坐标和纵坐标可以独立选择符号。这时候我们可以独立考虑问题,选择一组变量s1,…,sn,使得∑ni=1sidi=x,其中si=\plusmn1。
这个我们可以先将所有si赋予1,之后选择一个子集赋值为−1,这时候问题变成选择一个子集和正好为某个特定的数,这是一个简单的背包问题,但是需要用到一些特殊的优化技巧,从而做到O(nmaxni=1di)的时间复杂度。