CF1553(Div.1+Div.2) I 题解

这篇博客探讨了一个算法问题,涉及长度为n的排列p的阶梯序列。阶梯序列由排列p中每个位置的最长单调区间的长度组成。文章首先分析了问题的性质,然后提出了动态规划的解决方案。通过定义状态表示前i个区间的合并情况,博客详细解释了状态转移方程,并给出了O(n^2)的时间复杂度的算法实现。最后,提供了求解方案数的公式。

前言

由于 NTT 是 101010 级算法,所以本篇题解仅介绍到 O(n2)O(n^2)O(n2) 解法。

Description

给定一个长度为 nnn 的排列 ppp

令其中第 iii 个位置的权值为最长的包含 iii 的单调区间。例如,p=[4,1,2,3,7,6,5]p=[4,1,2,3,7,6,5]p=[4,1,2,3,7,6,5] 中,第 666 个位置的权值为 [5,7][5,7][5,7] 的长度,第 222 个位置的权值为 [2,4][2,4][2,4] 的长度。

将这些权值依次拼在一起,就得到了 ppp阶梯序列

给定 aaa,你需要求出存在多少个 ppp,使得 aaappp阶梯序列

1≤n≤1051 \le n \le 10^51n105,时限 10s\texttt{10s}10s,空限 1GB\texttt{1GB}1GB

Solution

Part 1: 性质的分析与观察

根据阶梯序列的定义,aia_iaiiii 所属的极长单调区间长度。但是 iii 既可以是这段区间的中间某个位置,也可以是两端的位置,考虑起来情况较多,较为困难。于是我们先考虑第一个位置,其只能作为单调区间的开头,思考起来较为容易。

x=a1x=a_1x=a1。不难发现,此时 [1,x][1,x][1,x] 是一个极长单调区间。接下来,我们再考虑 px+1p_{x+1}px+1,并令 x′=ax+1x'=a_{x+1}x=ax+1。不难发现,px+1p_{x+1}px+1 不能与 [1,x][1,x][1,x] 合并成一个新的单调区间,且 [x+1,x+x′][x+1,x+x'][x+1,x+x] 也是一个极长的单调区间。以此类推,我们将序列划分为了若干个极长的单调区间。这样一来,我们就成功地将问题转化为:给定若干个区间,你需要往里面填数,使得序列构成一个排列,且每个给定的区间都是一个极长的单调区间,求方案数。

如果区间只要求单调,不要求极长,那么问题就十分简单了。由于每个区间的 pip_ipi 在值域上都是连续的一段 [L,R][L,R][L,R],所以我们只需要钦定这些 [L,R][L,R][L,R] 的大小关系以及这些极长区间的增减性即可唯一确定 ppp。方案数不难通过组合数以及乘法原理写出。

若要求极长,问题变了复杂了起来。

注意到,若两段区间不可合并,那么限制会较为复杂;若必须合并,那么限制非常简单。这启发我们从反面考虑。但是,不可合并的相邻区间对太多了,这又启发我们容斥地计算。然而 nnn 较大,不能 2n2^n2n 地枚举相邻对。

能否 dp\text{dp}dp 呢?

Part 2: dp 的设计与转移

为方便叙述,定义 sis_isi 表示第 iii 个区间的长度。

fif_{i}fi 表示,仅考虑前 iii 个区间的方案数。

根据容斥的基本思想,对于第 i+1i+1i+1 个位置,它有两种选择:

  • violate the rule\text{violate the rule}violate the rule——它与前面的区间可以合并
  • follow the rule or not\text{follow the rule or not}follow the rule or not——无论它填什么都可以。

考虑动态地将第 i+1i+1i+1 个区间插入进前 iii 个区间的按值域左端点排序的序列。然而,无论对于前者(违反规则)还是后者(随意),插入的位置数似乎较难根据 iii 直接确定(若插入到某个不恰当的位置,可能会将原来两个合并的区间断开)。

仔细分析——对于前者而言,若前面一个区间长度为 111,那么插入的位置有 222 个,否则只有 111 个;对于后者而言,令 jjj 为前 iii 个区间中钦定可合并的次数,那么插入的位置树为 i−j+1i-j+1ij+1 个。

从而,我们加维度 jjj,表示目前钦定合并的次数。同时,再加一维 kkk,表示上一个区间的长度是否为 111

总结一下最终的状态设计:令 fi,j,kf_{i,j,k}fi,j,k 表示,看了前 iii 个区间,合并 jjj 次,上一个区间的长度为 111 或不为 111 的方案数。注意,这里的上一个区间长度不一定是 si−1s_{i-1}si1,而可能是一个比它更大的值(因为 si−1s_{i-1}si1 可能参与了多次合并,而使得上一个区间的长度较大)。

转移如下:

  • 钦定合并。
    fi−1,j,0→fi,j+1,0f_{i-1,j,0} \to f_{i,j+1,0}fi1,j,0fi,j+1,02fi−1,j,1→fi,j+1,02f_{i-1,j,1} \to f_{i,j+1,0}2fi1,j,1fi,j+1,0

  • 钦定合并。

    • si≠1s_i \neq 1si=1,则
      2(i−j)fi−1,j,0/1→fi,j,02(i-j)f_{i-1,j,0/1} \to f_{i,j,0}2(ij)fi1,j,0/1fi,j,0

    • si=1s_i=1si=1,则
      (i−j)fi−1,j,0/1→fi,j,1(i-j)f_{i-1,j,0/1} \to f_{i,j,1}(ij)fi1,j,0/1fi,j,1

这里容易出现一个错误,就是在转移的时候漏掉系数 222。由于长度超过 111 的区间的单调增与单调减是不同的,所以在向第三维为 000 的状态转移时,要额外乘上一个 222

边界:
①若 s1≠1s_1 \neq 1s1=1f1,0,0=2f_{1,0,0}=2f1,0,0=2
②若 s1=1s_1=1s1=1f1,0,1=1f_{1,0,1}=1f1,0,1=1
答案:

∑i=0len(−1)i(fn,i,0+fn,i,1)\sum_{i=0}^{\text{len}} (-1)^i (f_{n,i,0}+f_{n,i,1})i=0len(1)i(fn,i,0+fn,i,1)

时间复杂度 O(n2)O(n^2)O(n2)

//https://codeforces.ml/contest/1553/submission/123423493
for (int i=2;i<=len;i++){
	for (int j=0;j<=len;j++){
		chksum(f[i-1][j][0],f[i][j+1][0]);
		chksum(f[i-1][j][1],f[i][j+1][0],2);
		if (s[i]!=1){
			for (int k=0;k<2;k++)  chksum(f[i-1][j][k],f[i][j][0],2*(i-j));	
		}
		else{
			for (int k=0;k<2;k++)  chksum(f[i-1][j][k],f[i][j][1],i-j);
		}
	}
}
题面:# CF2122D Traffic Lights ## 题目描述 给定一个包含 $n$ 个顶点 $m$ 条边的简单无向连通图。 初始时刻(第 $0$ 秒时),令牌位于顶点 $1$。设经过 $t$ 秒后令牌位于顶点 $u$,每秒必须选择执行以下操作之一: - 等待 $1$ 秒; - 花费 $1$ 秒,让令牌沿顶点 $u$ 的第 $(t\bmod \text{deg}(u)+1)^*$ 条边移动(边的顺序按照输入顺序排列)。 请计算从顶点 $1$ 到顶点 $n$ 的最短总耗时,以及在总耗时最短的前提下能够实现的最小等待时间。 $^{\text{∗}}x \bmod y$ 表示 $x$ 除以 $y$ 的余数。 ## 输入格式 第一行输入一个整数 $t(1\leq t\leq 1000)$,表示测试用例数量。 对于每个测试用例第一行包括两个整数 $n,m$($2\leq n\leq 5000$,$n-1\leq m\leq \frac {n(n-1)}2$),表示图的点数和边数。 接下来 $m$ 行,第 $i$ 行包括 $u_i,v_i$($1\leq u_i,v_i\leq n$),表示第 $i$ 条边的两个顶点。 数据保证图是简单无向连通图(无重边无自环)。 保证所有测试用例的 $n$ 之和不超过 $5000$,$m$ 之和不超过 $5\times 10^5$。 ## 输出格式 对于每个测试用例,输出一行包括 $2$ 个整数,分别表示最短总耗时,以及在总耗时最短前提下能够实现的最小等待时间。 ## 输入输出样例 #1 ### 输入 #1 ``` 2 6 6 1 2 2 3 3 4 4 6 1 5 5 6 4 3 1 2 1 3 1 4 ``` ### 输出 #1 ``` 4 2 3 0 ``` ## 说明/提示 **【样例解释】** 第一个测试用例的最优策略如下: - $0$ 秒时,等待 $1$ 秒; - $1$ 秒时,将令牌从顶点 $1$ 移动到顶点 $5$; - $2$ 秒时,等待 $1$ 秒; - $3$ 秒时,将令牌从顶点 $5$ 移动到顶点 $6$。 第二个测试用例的最优策略如下: - $0$ 秒时,将令牌从顶点 $1$ 移动到顶点 $2$; - $1$ 秒时,将令牌从顶点 $2$ 移动到顶点 $1$; - $2$ 秒时,将令牌从顶点 $1$ 移动到顶点 $4$。 题解div2 D 给创飞了。 别笑,你也过不了 div2 D。 我们充分发扬人类智慧,猜测最终的时间绝对不大。 所以我们直接设 dp i,j ​ 为 i 时间,到达 j 的等待时间最小值,dp 0,1 ​ =0。 转移很显然:dp i,x ​ ←dp i−1,x ​ +1,然后 dp i−1,x ​ → min dp i,E i,∣E i ​ ∣modx ​ ​ ,其中 E i ​ 为 i 的边集。 也就是说,先考虑不动的情况,接着对于每个点考虑沿着当前可以走的边走一步的情况。 于是强大的 Twilight_star 就这样在最后三分钟过了这道题,Rating 上了 2300。 code:https://codeforces.com/contest/2122/submission/329914223。 考虑证明: 很显然,到达了一个点 x 的最坏情况是 deg x ​ 秒后才到达下一条想走的边。其中 deg 是度数。 那么这样一条路径上的最坏时间情况是 ∑ i∈P ​ deg i ​ 的,其中 P 是路径。 先不考虑时间,再考虑一个性质,一条最短路路径外的点最多连 3 个路径内的点。 为什么?因为假如连了 4 个及以上,路径就可以更短,不符合最短路的性质。 那么,假设路径长为 k,那么 ∑ i∈P ​ deg i ​ 最多也就 2×k+(n−k)。 因此,总消耗的时间是 O(n) 级别的,时间复杂度为 O(∑n 2 +m)。 这种题做不出来还不是我的问题,渡劫场遇到这么搞笑的事,菜死了。解释一下DP转移方程
最新发布
08-27
### **问题概述** 我们需要在给定的无向连通图中,从顶点 `1` 出发,通过移动或等待操作,以最短时间到达顶点 `n`,并在最短时间的前提下最小化等待时间。移动操作的规则是: - 在第 `t` 秒时,若位于顶点 `u`,可以选择沿 `u` 的第 `(t mod deg(u) + 1)` 条边移动(边的顺序按输入顺序排列)。 --- ### **DP 状态定义** 定义 `dp[i][j]`: - **含义**:在 **第 `i` 秒** 时,位于顶点 `j` 的 **最小等待时间**。 - **初始状态**:`dp[0][1] = 0`(第 `0` 秒时位于起点 `1`,初始等待时间为 `0`)。 --- ### **DP 转移方程** #### **1. 等待操作** - **转移**:如果在第 `i-1` 秒位于 `j`,可以选择等待 `1` 秒,此时: ```python dp[i][j] = min(dp[i][j], dp[i-1][j] + 1) ``` - **解释**:等待时间增加 `1`,位置不变。 #### **2. 移动操作** - **转移**:如果在第 `i-1` 秒位于顶点 `x`,且 `x` 的第 `k` 条边连接 `y`(其中 `k = (i-1) mod deg(x) + 1`),则可以从 `x` 移动到 `y`: ```python dp[i][y] = min(dp[i][y], dp[i-1][x]) ``` - **解释**:移动不增加等待时间(因为移动操作本身消耗 `1` 秒,但不算等待时间)。 --- ### **算法流程** 1. **初始化**: - 所有 `dp[i][j]` 初始化为 `INF`,`dp[0][1] = 0`。 2. **时间迭代**: - 对时间 `i` 从 `1` 到 `T_max`(猜测的最大时间)枚举: - **先处理等待操作**:更新 `dp[i][j]`。 - **再处理移动操作**:根据当前时间 `i-1` 和顶点 `x` 的度数 `deg(x)`,确定移动的边 `k`,更新 `dp[i][y]`。 3. **终止条件**: - 当 `dp[i][n]` 首次被更新时,记录 `i` 为最短时间,`dp[i][n]` 为最小等待时间。 --- ### **时间复杂度分析** - **时间上限**: - 最坏情况下,路径长度 `k` 不超过 `n`,每条边的等待时间不超过 `deg(x)`,总时间为 `O(n + sum(deg)) ≈ O(n + 2m) = O(m)`。 - 因 `n ≤ 5000`,`m ≤ 5e5`,总时间为 `O(nm)`,可通过。 --- ### **样例解释(输入 #1)** - **路径**:`1 → 5 → 6`,总时间 `4` 秒,等待时间 `2` 秒: - 第 `0` 秒:等待(`+1` 等待时间)。 - 第 `1` 秒:从 `1` 移动至 `5`(`k = (1 mod 2) + 1 = 2`,选择第 `2` 条边 `1-5`)。 - 第 `2` 秒:等待(`+1` 等待时间)。 - 第 `3` 秒:从 `5` 移动至 `6`(`k = (3 mod 2) + 1 = 2`,选择第 `2` 条边 `5-6`)。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值