有向图,求图上点数最小的正环的大小。
f d , i , j f_{d, i, j} f d , i , j :至多 2 d 2^{d} 2 d 边,从 i i i 到 j j j 的最大权值。
跳跃式倍增尝试使用 x x x 条边,是否存在正环,即是否存在 g i , i > 0 g_{i, i}>0 g i , i > 0 。
题意:给定 N , K N,K N , K ,求满足存在 i i i ,使得 2 a i ≡ ∑ a i ( m o d K ) 2 a_{i} \equiv \sum a_{i} \pmod K 2 a i ≡ ∑ a i ( m o d K ) 的序列数量。
容斥,求对于任意的 i i i ,都满足 2 a i ≠ S 2a_i \neq S 2 a i = S 的方案数。
枚举 S S S ,设 f i , j f_{i, j} f i , j 为前 i i i 位,数字和是 j j j 的方案数。
转移:f i , j + l ← f i − 1 , l f_{i, j + l} \gets f_{i - 1, l} f i , j + l ← f i − 1 , l ,其中 2 l ≢ S 2l \not\equiv S 2 l ≡ S 。
【快速幂】定义转移矩阵 T T T ,其中 T j , j + l = [ 2 l ≠ S ] T_{j, j + l} = [2 l \neq S] T j , j + l = [ 2 l = S ] 。设 f 0 = [ 1 , 0 , … , 0 ] f_{0}=[1, 0, \dots, 0] f 0 = [ 1 , 0 , … , 0 ] ,答案是 f 0 × T n f_{0} \times T^n f 0 × T n 的第 S S S 项,复杂度 O ( K 4 log N ) \mathcal{O}(K^4\log N) O ( K 4 log N ) 。
【卷积优化】定义转移多项式 g g g ,其中第 l l l 项系数 g l = [ 2 l ≠ S ] g_{l} = [2 l \neq S] g l = [ 2 l = S ] ,设 f 0 = 1 f_{0}=1 f 0 = 1 ,答案是 f 0 × g n f_{0} \times g^n f 0 × g n ,复杂度 O ( K 3 log N ) \mathcal{O}(K^{3}\log N) O ( K 3 log N ) 。
构造一个长度为 N × M N \times M N × M 的合法括号序列。第 i i i 个位置上的左括号代价为 A i m o d N A_{i\bmod N} A i m o d N ,右括号代价为 B i m o d N B_{i\bmod N} B i m o d N 。求最小代价。(1 ≤ N ≤ 20 , 1 ≤ M ≤ 1 0 7 1\leq N\leq 20, 1\leq M\leq 10^7 1 ≤ N ≤ 2 0 , 1 ≤ M ≤ 1 0 7 )
设 f i , j f_{i,j} f i , j 为到第 i i i 的位置、有 j j j (j ≤ N j\leq N j ≤ N )个左括号未被匹配的最小代价。
{ f i , j + 1 ← f i − 1 , j + A i m o d N f i , j − 1 ← f i − 1 , j + B i m o d N \begin{cases} f_{i, j+1} \gets f_{i-1,j}+A_{i\bmod N} \\ f_{i, j-1} \gets f_{i-1,j}+B_{i\bmod N} \\ \end{cases} { f i , j + 1 ← f i − 1 , j + A i m o d N f i , j − 1 ← f i − 1 , j + B i m o d N
其中 f 0 , 0 = 0 , f 0 , j = e f_{0,0}=0, f_{0,j}=e f 0 , 0 = 0 , f 0 , j = e 。其中 e e e 是 min \min min 的幺元,即 + ∞ +\infty + ∞ 。
设
T i = [ e A i ⋯ e B i e A i ⋮ B i e A i ⋮ B i ⋱ e ⋯ ] ( N + 1 ) × ( N + 1 ) T_i = \begin{bmatrix} e & A_{i} & & \cdots & e \\ B_{i} & e & A_{i} & & \vdots \\ & B_{i} & e & A_{i} \\ \vdots & & B_{i} & \ddots \\ e & \cdots \\ \end{bmatrix}_{(N+1)\times (N+1)} T i = ⎣ ⎢ ⎢ ⎢ ⎢ ⎢ ⎢ ⎢ ⎡ e B i ⋮ e A i e B i ⋯ A i e B i ⋯ A i ⋱ e ⋮ ⎦ ⎥ ⎥ ⎥ ⎥ ⎥ ⎥ ⎥ ⎤ ( N + 1 ) × ( N + 1 )
设 A 0 = ( 0 , e , … , e ) A_0=(0, e, \dots, e) A 0 = ( 0 , e , … , e ) ,答案为 A M N = A 0 ( T 1 T 2 … T N ) M A_{MN}= A_0 (T_{1}T_{2} \dots T_{N})^M A M N = A 0 ( T 1 T 2 … T N ) M 的第 0 项。
经典 DP LPS ( S ) = LCS ( S , reverse ( S ) ) \text{LPS}(S) = \text{LCS}(S, \text{reverse}(S)) LPS ( S ) = LCS ( S , reverse ( S ) )
给定一个长度为 N N N 的字符串,为变成回文串,最少需要插入多少字符? 给定一个长度为 N N N ,有 K K K 种类型的括号的字符串,为变成 RBS,最少需要插入多少字符?(注意区分 K = 1 K=1 K = 1 与 K ⩾ 2 K \geqslant 2 K ⩾ 2 ) 本质不同的非空回文子序列数量 s [ i ] ≠ s [ j ] s[i] \neq s[j] s [ i ] = s [ j ] 时,f [ i ] [ j ] = f [ i + 1 ] [ j ] + f [ i ] [ j − 1 ] − f [ i + 1 ] [ j − 1 ] f[i][j] = f[i+1][j] + f[i][j-1] - f[i+1][j-1] f [ i ] [ j ] = f [ i + 1 ] [ j ] + f [ i ] [ j − 1 ] − f [ i + 1 ] [ j − 1 ] s [ i ] = s [ j ] s[i] = s[j] s [ i ] = s [ j ] 时中间没有字符满足 s [ x ] = s [ i ] s[x]=s[i] s [ x ] = s [ i ] ,f [ i ] [ j ] = 2 × f [ i + 1 ] [ j − 1 ] + 2 f[i][j] = 2 \times f[i+1][j-1] + 2 f [ i ] [ j ] = 2 × f [ i + 1 ] [ j − 1 ] + 2 中间只有一个字符满足 s [ x ] = s [ i ] s[x]=s[i] s [ x ] = s [ i ] ,f [ i ] [ j ] = 2 × f [ i + 1 ] [ j − 1 ] + 1 f[i][j] = 2 \times f[i+1][j-1] + 1 f [ i ] [ j ] = 2 × f [ i + 1 ] [ j − 1 ] + 1 中间有两个或以上字符满足 s [ x ] = s [ i ] s[x]=s[i] s [ x ] = s [ i ] ,记最左最右的分别为 l , r l,r l , r ,f [ i ] [ j ] = 2 × f [ i + 1 ] [ j − 1 ] − f [ l + 1 ] [ r − 1 ] f[i][j] = 2 \times f[i+1][j-1] - f[l+1][r-1] f [ i ] [ j ] = 2 × f [ i + 1 ] [ j − 1 ] − f [ l + 1 ] [ r − 1 ] 给定权值 V V V 和排列 A , B A, B A , B 。
设集合 U U U 。依次进行 N N N 次操作,每次任选一种:
找到当前的最小下标 i i i 使得 A i ∈ S A_{i} \in S A i ∈ S ,将 A i A_{i} A i 从 S S S 中移除,将 A i A_{i} A i 加入 U U U 。 找到当前的最小下标 i i i 使得 B i ∈ S B_i \in S B i ∈ S ,将 B i B_i B i 从 S S S 中移除。 求在所有可能的合法操作序列中,最大化 ∑ k ∈ U V k \sum_{k\in U}V_{k} ∑ k ∈ U V k 。
方法:DP 充要条件:对于任意一对满足 j < i j < i j < i (即元素 A j A_j A j 在排列 A A A 中出现在 A i A_i A i 的前面)且 B A j − 1 > B A i − 1 B^{-1}_{A_j} > B^{-1}_{A_i} B A j − 1 > B A i − 1 (即元素 A j A_j A j 在排列 B B B 中出现在 A i A_i A i 的后面)的下标对 ( i , j ) (i, j) ( i , j ) ,如果 A i ∈ U A_i \in U A i ∈ U ,那么必须有 A j ∈ U A_j \in U A j ∈ U 。
逆否命题:如果 A i ∈ U A_i \in U A i ∈ U ,且 A j ∉ U A_j \not \in U A j ∈ U ,那么它们不能同时满足 j < i j < i j < i 且 B A j − 1 > B A i − 1 B^{-1}_{A_j} > B^{-1}_{A_i} B A j − 1 > B A i − 1 。
按 i i i 从小到大进行决策,考虑所有满足 A j ∉ U A_j \not \in U A j ∈ U 的 j j j ,由于天然满足 j < i j < i j < i ,则不能满足 B A j − 1 > B A i − 1 B^{-1}_{A_j} > B^{-1}_{A_i} B A j − 1 > B A i − 1 ,即必须 max j : A j ∉ U B A j − 1 ⩽ B A i − 1 \max_{j:A_j \not \in U} B^{-1}_{A_j} \leqslant B^{-1}_{A_i} max j : A j ∈ U B A j − 1 ⩽ B A i − 1 。
考虑设 f ( i , x ) f(i, x) f ( i , x ) 表示已决策前 i i i 个元素,且 x = max j : A j ∉ U B A j − 1 x=\max_{j:A_j \not \in U} B^{-1}_{A_j} x = max j : A j ∈ U B A j − 1 时的最优解。
若 A i ∉ U A_{i} \not\in U A i ∈ U ,f ( i , max ( x , B A j − 1 ) ) ← f ( i − 1 , x ) f(i, \max(x, B^{-1}_{A_j})) \gets f(i - 1, x) f ( i , max ( x , B A j − 1 ) ) ← f ( i − 1 , x ) ; 若 A i ∈ U A_{i} \in U A i ∈ U ,当 x ⩽ B A i − 1 x \leqslant B^{-1}_{A_i} x ⩽ B A i − 1 时,有 f ( i , x ) ← f ( i − 1 , x ) + V A i f(i, x) \gets f(i-1, x) + V_{A_i} f ( i , x ) ← f ( i − 1 , x ) + V A i 。 初始值:f ( 0 , 0 ) = 0 f(0, 0) = 0 f ( 0 , 0 ) = 0 。 答案:max f ( N , ∗ ) \max f(N, *) max f ( N , ∗ ) 。
用线段树维护 f ( x ) f(x) f ( x ) ,转移即区间最值、单点修改、区间加。
1 2 3 4 5 6 7 8 9 10 11 std::vector<Info> init (N + 1 ) ;init[0 ] = {0 }; LazySegmentTree<Info, Lazy> seg (init) ;for (int i = 0 ; i < N; i++) { int p = invB[A[i]]; auto [M] = seg.query (0 , p + 1 ); seg.update (0 , p + 1 , {V[A[i]]}); auto [cur_p] = seg.query (p, p + 1 ); seg.modify (p, {std::max (M, cur_p)}); } auto [ans] = seg.query (0 , N + 1 );
方法:最大权闭合子图 充要条件:如果选择了 A i ∈ U A_i \in U A i ∈ U ,那么对于所有满足 j < i j < i j < i 且 B A j − 1 > B A i − 1 B^{-1}_{A_j} > B^{-1}_{A_i} B A j − 1 > B A i − 1 的 j j j ,必须有 A j ∈ U A_j \in U A j ∈ U 。
即选了 A i A_i A i ,就必须选 A j A_j A j 。
所有 V k > 0 V_k > 0 V k > 0 的点与源点 S S S 相连,容量 V k V_k V k 。 所有 V k < 0 V_k < 0 V k < 0 的点与汇点 T T T 相连,容量 ∣ V k ∣ |V_k| ∣ V k ∣ 。 如果选 i i i 必须选 j j j (即 j < i j < i j < i 且 B A j − 1 > B A i − 1 B^{-1}_{A_j} > B^{-1}_{A_i} B A j − 1 > B A i − 1 ),则从 i i i 向 j j j 连边,容量 + ∞ +\infty + ∞ 。 最终的最大收益 = ∑ V k > 0 V k − = \sum_{V_k > 0} V_k - = ∑ V k > 0 V k − 最小割,表示满足依赖关系,我们不得不放弃的正权值,或者被迫承受的负权值。
代码的遍历顺序是 i i i 从 0 0 0 到 N − 1 N-1 N − 1 ,这意味着天然满足了 j < i j < i j < i 的条件。当我们处理到第 i i i 个元素时,之前遍历过的元素都是候选的 j j j 。
遇到负权点时,相当于在图中增加了一个与汇点 T T T 相连的节点。将其容量记录在 std::map 中,键为其在 B B B 中的位置 p = B A i − 1 p=B^{-1}_{A_i} p = B A i − 1 。这些记录下来的负权值,就是后续正权点可以用来推流的目标。
遇到正权点时,首先将其乐观地全部加入 max_profit(即公式中的 ∑ V k > 0 V k \sum_{V_k > 0} V_k ∑ V k > 0 V k )。接下来,由于它可能依赖于前面遍历过的负权点,我们需要为其寻找能够割掉的代价。
根据条件 B A j − 1 > B A i − 1 B^{-1}_{A_j} > B^{-1}_{A_i} B A j − 1 > B A i − 1 ,当前正权点只能流向位置大于 p = B A i − 1 p=B^{-1}_{A_i} p = B A i − 1 的负权点。因此,使用 capacity.lower_bound(p) 查找所有满足条件的负权点。从满足的最小的 B A j − 1 B^{-1}_{A_j} B A j − 1 开始,尝试用当前的 flow 去扣除负权点的容量,并同时从 max_profit 中减去这部分流出的代价。
填加乘 在序列中填加号或乘号,使运算结果恰好等于 m m m 。1 ⩽ n ⩽ 2 × 1 0 5 , 0 ⩽ a i ⩽ 1 0 4 , 1 ⩽ m ⩽ 1 0 4 1 \leqslant n\leqslant 2\times10^{5},\ 0 \leqslant a_{i} \leqslant 10^{4},\ 1 \leqslant m\leqslant 10^{4} 1 ⩽ n ⩽ 2 × 1 0 5 , 0 ⩽ a i ⩽ 1 0 4 , 1 ⩽ m ⩽ 1 0 4 。
乘法的优先级高,设 d p i , x dp_{i,x} d p i , x 表示处理到第 i i i 个数,
转移分为 a i a_{i} a i 前面填加号或乘号两种。具体见代码。
暴力转移的复杂度是对的,因为至多 20 个数乘起来就超过 1 0 6 10^{6} 1 0 6 了,键不会很多,转移量在 log m \log m log m 级别,每次转移是 bitset 的复杂度 log m \log m log m 。最终时间复杂度 Θ ( n log 2 m ) \Theta(n\log^{2}m) Θ ( n log 2 m ) ,空间复杂度 Θ ( log 2 m ) \Theta(\log^{2}m) Θ ( log 2 m ) 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 constexpr int N = 1e4 + 8 ;map<int , bitset<N>> dp; dp[a[0 ]] = 1 ; for (int i = 1 ; i < n; i++) { map<int , bitset<N>> ndp; for (auto & [x, mp] : dp) { ndp[min (m + 1 , x * a[i])] |= mp; } for (auto & [x, mp] : dp) { ndp[a[i]] |= mp << x; } dp = ndp; } bitset<N> res; for (auto & [x, mp] : dp) { res |= mp << x; } cout << (res[m] ? "YES" : "NO" ) << endl;
第十届中国大学生程序设计竞赛总决赛 G. + and × with a sugar / + , × 与糖 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 int n;cin >> n; deque<int > a (n) ;for (int i = 0 ; i < n; i++) { cin >> a[i]; } int sum = 0 ;while (!a.empty () && a.back () == 1 ) { sum++; a.pop_back (); } while (!a.empty () && a.front () == 1 ) { sum++; a.pop_front (); } bool flag = false ;ll res = 1 ; for (int i = 0 ; i < a.size (); i++) { res *= a[i]; res %= mod; if (res > 2 * n) { flag = true ; } } if (n == 0 || flag) { cout << (res + sum) % mod << endl; continue ; } map<int , int > f; f[a[0 ]] = 0 ; for (int i = 1 ; i < a.size (); i++) { map<int , int > nf; for (auto & [k, v] : f) { chmax (nf[k * a[i]], v); chmax (nf[a[i]], v + k); } f = nf; } int ans = 0 ;for (auto & [k, v] : f) { chmax (ans, v + k); } cout << ans + sum << endl;