Hello 2020
B. New Year and Ascent Sequence
这题实际上是一个计数问题,要求任意子序列连接起来,构成上升序列
任意子序列处理起来比较麻烦,我们把问题转换成为整个序列单调不降,任意子序列都是单调不降的
此时序列个数为 C C C ,最后的答案就是 n 2 − C n^2 - C n 2 − C
这样我们任意一个序列就可以用二元组 ( l i , r i ) (l_i, r_i) ( l i , r i ) 表示,l i ← A [ 0 ] , r i ← A [ n − 1 ] l_i \leftarrow A[0], r_i \leftarrow A[n-1] l i ← A [ 0 ] , r i ← A [ n − 1 ]
问题转换为求解满足 r i ⩾ l j , ( i , j ) r_i \geqslant l_j, \quad (i, j) r i ⩾ l j , ( i , j ) 的序对
for ∀ j : x ← l j \textbf{for} \ \forall j: \ x \leftarrow l_j for ∀ j : x ← l j ,在所有数中二分查找
\quad 找到第一个满足 r i ⩾ x r_i \geqslant x r i ⩾ x 的 r i r_i r i ,[ r i , e n d ) [r_i, end) [ r i , e n d ) 即为能够取的所有值
这里如果用一个二元组表示,要将 r i r_i r i 作为第一关键字,即 ( r i , l i ) (r_i, l_i) ( r i , l i )
C. New Year and Permutation
这个问题比较 tricky ,其实观察后发现
如果 [ l , r ] [l, r] [ l , r ] 是满足条件的子列,那么 [ min i = l r p i ⋯ max i = l r p i ] [\min_{i = l}^{r} p_i \cdots \max_{i = l}^{r} p_i] [ min i = l r p i ⋯ max i = l r p i ] 属于 [ l , r ] [l, r] [ l , r ] 这个子段
所以可以将这个子段缩成一个点,子段的长度为 l e n = r − l + 1 len = r-l+1 l e n = r − l + 1
长度为 n n n 的排列一共有 n − l e n + 1 n-len+1 n − l e n + 1 种长度为 l e n len l e n 的子段
将子段缩点,剩下有 n − l e n n-len n − l e n 个数,用隔板法插入,一共有 n − l e n + 1 n-len+1 n − l e n + 1 种插入方式
所以最后答案就是 ( n − l e n + 1 ) ⋅ ( n − l e n + 1 ) ⋅ ( l e n ) ! ⋅ ( n − l e n ) ! (n-len+1)\cdot (n-len+1) \cdot (len)! \cdot (n-len)! ( n − l e n + 1 ) ⋅ ( n − l e n + 1 ) ⋅ ( l e n ) ! ⋅ ( n − l e n ) !
D. New Year and Conference
题意,一门课有两个上课地点 a , b a, b a , b ,在两个上课地点的时间不同,分别为 [ s a i , e a i ] , [ s b i , e b i ] [sa_i, ea_i], [sb_i, eb_i] [ s a i , e a i ] , [ s b i , e b i ]
定义 venue-sensitive \text{venue-sensitive} venue-sensitive 如下,存在课程集合 S S S ,size ( S ) ⩾ 2 \textbf{size}(S) \geqslant 2 size ( S ) ⩾ 2
使得 S S S 中的所有课在地点 a a a 上课冲突,在 b b b 上不冲突;或者在 b b b 冲突,在 a a a 不冲突
如果找不到 venue-sensitive \text{venue-sensitive} venue-sensitive 的课程集合,输出 yes \text{yes} yes ,否则输出 no \text{no} no
对于课程 i i i 用一个四元组 ( s a i , e a i , s b i , e b i ) (sa_i, ea_i, sb_i, eb_i) ( s a i , e a i , s b i , e b i ) 表示,大致思路为
先找出 [ s a , e a ] [sa, ea] [ s a , e a ] 存在重合(即在 a a a 上课重合)的集合 S S S ,然后判断 S S S 中是否存在一组 ( i , j ) , i ≠ j (i, j), i \neq j ( i , j ) , i = j
使得 [ s b i , e b i ] , [ s b j , e b j ] [sb_i, eb_i], \ [sb_j, eb_j] [ s b i , e b i ] , [ s b j , e b j ] 不重合 ,即存在有 在 b b b 上课不重合的两门课
如果找到了,说明存在 venue-sensitive \text{venue-sensitive} venue-sensitive ,返回 true \text{true} true
活动选择模型,可以先根据 s a i sa_i s a i 对所有活动排序,用 STL \text{STL} STL 容器 S S S 来维护所有活动 [ s a 1 ⋯ s a n ] [sa_1 \cdots sa_n] [ s a 1 ⋯ s a n ]
对于活动 i i i ,[ s a i , e a i ] [sa_i, ea_i] [ s a i , e a i ] ,在 S S S 中二分查找位置 p p p ,使得 s a p > e a i sa_p > ea_i s a p > e a i
此时有 s a i ⩽ s a i + 1 ⩽ ⋯ ⩽ s a p − 1 ⩽ e a i sa_i \leqslant sa_{i+1} \leqslant \cdots \leqslant sa_{p-1} \leqslant ea_i s a i ⩽ s a i + 1 ⩽ ⋯ ⩽ s a p − 1 ⩽ e a i
也就是说,活动 i , [ s a i , e a i ] i, \ [sa_i, ea_i] i , [ s a i , e a i ] 与活动集 V = [ i + 1 , p − 1 ] V = [i+1, p-1] V = [ i + 1 , p − 1 ] 在地点 a a a 相交
接下来要判断活动 i , [ s b i , e b i ] i, \ [sb_i, eb_i] i , [ s b i , e b i ] 与活动集 V = [ i + 1 , p − 1 ] V=[i+1, p-1] V = [ i + 1 , p − 1 ] 在地点 b b b 是否相交
可以在一开始先针对地点 b b b 的活动建一个线段树
只需在线段树中查询区间 [ i + 1 , p − 1 ] [i+1, p-1] [ i + 1 , p − 1 ] 的 maxS , minE \text{maxS}, \ \text{minE} maxS , minE
如果 e b i < maxS or s b i > minE eb_i < \text{maxS} \ \textbf{or} \ sb_i > \text{minE} e b i < maxS or s b i > minE
说明 V V V 中存在一个活动和活动 i i i 不重合,集合是 venue-sensitive \text{venue-sensitive} venue-sensitive 的
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 class SegTree { public: void build(int p, int l, int r) { t[p].l = l, t[p].r = r; if (l >= r) { t[p].maxS = a[l].sb; t[p].minE = a[l].eb; return ; } int mid = (l + r) >> 1; build(p<<1, l, mid); build(p<<1 |1, mid+1, r); pull(p); } }; bool solve () { sort(a+1, a+1+n); // according to eb seg.build(1, 1, n); vector<int> V; V.push_back(0); for (int i = 1; i <= n; i++) V.push_back(a[i].sa); for (int i = 1; i <= n; i++) { int p = upper_bound(V.begin(), V.end(), a[i].ea) - V.begin(); p--; int maxS = seg.find(1, i+1, p, 0); int minE = seg.find(1, i+1, p, 1); if (a[i].eb < maxS || a[i].sb > minE) return false ; } return true ; }
codeforces round 714 div2
F. Swapping Problem
很显然,对于最左边的情况,不论交换前后,绝对值都为 p i + 2 ⋅ over + p j p_i + 2\cdot \text{over} + p_j p i + 2 ⋅ over + p j
其中 p i , p j p_i, p_j p i , p j 表示两条线段不相交部分,over \text{over} over 表示相交部分,结果不可能变好
能够使得结果变好的情况,按照向量方向为从小到大,如图所示 ,可以分为两类
根据 A i < B i A_i < B_i A i < B i 和 B i < A i B_i < A_i B i < A i 分为两类,{ X : A i < B i } , { Y : B i < A i } \{X: A_i < B_i\}, \ \{Y: B_i < A_i\} { X : A i < B i } , { Y : B i < A i }
a n s = ∑ abs i − 2 ⋅ over , over ans = \sum {\text{abs}_i} - 2 \cdot \text{over}, \quad \text{over} a n s = ∑ abs i − 2 ⋅ over , over 表示线段相交部分的长度
这样可以初步设计出一种算法
对于 A i < B i A_i < B_i A i < B i ,固定 A i A_i A i ,在满足 B j ⩽ A i B_j \leqslant A_i B j ⩽ A i 的条件下,找到最大的 A j A_j A j
这样使得线段overlap部分最大 ,即 over ( ( A i , B i ) , ( B j , A j ) ) \text{over}((A_i, B_i), (B_j, A_j)) over ( ( A i , B i ) , ( B j , A j ) ) 最大
对于 B i < A i B_i < A_i B i < A i ,也是类似的
具体的算法如下
每个集合 X ( A i → B i ) , Y ( B i → A i ) X(A_i \to B_i), \quad Y(B_i \to A_i) X ( A i → B i ) , Y ( B i → A i ) 拆成两个集合,以便于之后按照第一关键字排序
X 1 ( A i , B i ) , X 2 ( B i , A i ) , Y 1 ( B i , A i ) , Y 2 ( A i , B i ) X1(A_i, B_i), X2(B_i, A_i), Y1(B_i, A_i), Y2(A_i, B_i) X 1 ( A i , B i ) , X 2 ( B i , A i ) , Y 1 ( B i , A i ) , Y 2 ( A i , B i ) ,之后根据第一关键字排序
A i < B i A_i < B_i A i < B i 的情况,遍历集合 X 1 X1 X 1 ,对于 ∀ A i \forall A_i ∀ A i :
在集合 Y Y Y 中,找到满足 B j ⩽ A i B_j \leqslant A_i B j ⩽ A i 的最大的 B j B_j B j
此时 B j B_j B j 是关键字,所以 B j ∈ Y 1 B_j \in Y1 B j ∈ Y 1
遍历满足条件的 ∀ B j ∈ Y 1 \forall B_j \in Y1 ∀ B j ∈ Y 1 ,将 B j B_j B j 所对应的 A j A_j A j 值用一个 set \textbf{set} set 来维护
取 A j = set . r b e g i n ( ) A_j = \textbf{set}.rbegin() A j = set . r b e g i n ( ) ,over ← max { min ( A j , B i ) − A i } \text{over} \leftarrow \max \{\min(A_j, B_i) - A_i\} over ← max { min ( A j , B i ) − A i }
B i < A i B_i < A_i B i < A i 的情况也类似
最后 ∑ i ∣ A i − B i ∣ − 2 ⋅ over \sum_i |A_i - B_i| - 2\cdot \text{over} ∑ i ∣ A i − B i ∣ − 2 ⋅ over 就是答案
Add One
首先很容易想到一种数位dp的方法
一开始的时候,统计 x x x 有那些数字,以第几次操作作为”阶段“,f ( i , b ) f(i, b) f ( i , b ) 表示第 i i i 阶段,数字 b b b 的长度
f ( 0 , b ) = 1 , b ∈ digit ( x ) f ( i , b + 1 ) = f ( i , b + 1 ) + f ( i − 1 , b ) b ∈ [ 0 , 8 ] f ( i , 0 ) = f ( i , 0 ) + f ( i − 1 , 9 ) f ( i , 1 ) = f ( i , 1 ) + f ( i − 1 , 9 ) b = 9 \begin{aligned}
&f(0, b) = 1, \quad b \in \text{digit}(x) \\
&f(i, b+1) = f(i, b+1) + f(i-1, b) \quad b \in [0, 8] \\
&f(i, 0) = f(i, 0) + f(i-1, 9) \quad f(i, 1) = f(i, 1) + f(i-1, 9) \quad b = 9
\end{aligned}
f ( 0 , b ) = 1 , b ∈ digit ( x ) f ( i , b + 1 ) = f ( i , b + 1 ) + f ( i − 1 , b ) b ∈ [ 0 , 8 ] f ( i , 0 ) = f ( i , 0 ) + f ( i − 1 , 9 ) f ( i , 1 ) = f ( i , 1 ) + f ( i − 1 , 9 ) b = 9
但这样每个询问都必须重新计算遍,会超时,这里可以反着来 d p dp d p
第 m m m 次操作视为阶段 0 0 0 ,因为每一次操作对数位的影响 ,是相互独立的,所以算法正确
b ∈ [ 0 , 8 ] b \in [0, 8] b ∈ [ 0 , 8 ] ,第 i i i 阶段数字 b b b 的长度和第 i − 1 i-1 i − 1 阶段数字 b + 1 b+1 b + 1 的长度相等
f ( i , b ) = f ( i − 1 , b + 1 ) f(i, b) = f(i-1, b+1) f ( i , b ) = f ( i − 1 , b + 1 )
b = 9 b = 9 b = 9 ,第 i i i 阶段数字是 9 9 9 ,那么第 i − 1 → i i-1 \to i i − 1 → i 阶段存在 ( 0 → 9 ) , ( 1 → 9 ) (0 \to 9), (1 \to 9) ( 0 → 9 ) , ( 1 → 9 ) 的转移
换句话说,第 i i i 阶段 9 9 9 的长度来源于第 i − 1 i-1 i − 1 阶段 0 , 1 0, 1 0 , 1 贡献的长度
f ( i , 9 ) = f ( i − 1 , 0 ) + f ( i − 1 , 0 ) f(i, 9) = f(i-1, 0) + f(i-1, 0) f ( i , 9 ) = f ( i − 1 , 0 ) + f ( i − 1 , 0 )
最后统计 b ∈ [ 0 , 9 ] b \in [0, 9] b ∈ [ 0 , 9 ] ,看看 b b b 中那些数在 x x x 的数位中出现,对 f ( m , b ) f(m, b) f ( m , b ) 求和即可