时间之书(华中地区邀请赛 19th)
提供一种不一样的构造方法。
有一个比较直接的思路,即维护每一个点所属编号,然后找到每一个三角形三个端点所属编号,可以通过获取顶点编号进行 update 和 query 操作。用一个 map 存每个编号所对应的点的权值。对于 extend,不用做任何事情。
现在问题是,要将这些节点赋什么编号。有一个想法是,让每个三角形的三个顶点分别赋上 ( i , A ) , ( i , B ) , ( i , C ) (i,A),(i,B),(i,C) ( i , A ) , ( i , B ) , ( i , C ) 的编号。这个想法的问题在于一个点可能是若干个三角形的公共顶点。这样重复编号会使得 update 操作需要改变以该点为顶点的所有三角形的对应编号对应值。这很麻烦,而且可能要修改很多三角形。有没有什么不重不漏的修改方法呢?
如下图,这是 extend 一个初始三角形两层的示意图:
为了方便,本章节将 1 1 1 个点朝上,2 2 2 个点分别朝下方左右的称为 正三角形 , 1 1 1 个点朝下,2 2 2 个点分别朝上方左右的称为 倒三角形 。请勿与一般的正三角形概念混淆。
绿色点是大三角形的三个顶点,红色点是第一层 extend 新增的三个点,蓝色点是第二层 extend 新增的九个点。不难发现,除了大三角形的三个顶点,每一层新增的点集相当于上一层新增的正三角形的边中点点集。
在上图中,红色点点集 { D , E , F } \{D,E,F\} { D , E , F } 恰好为第零层大三角形的三条边的中点,蓝色点点集 { G , H , I , J , K , L , M , N , P } \{G,H,I,J,K,L,M,N,P\} { G , H , I , J , K , L , M , N , P } 恰好为第一层三个正三角形(即 △ A E F / 4 , △ B D F / 5 , △ C D E / 7 \triangle AEF/4,\triangle BDF/5,\triangle CDE/7 △ A E F /4 , △ B D F /5 , △ C D E /7 )的三条边的中点。
根据这个性质,我们可以规定每轮新增的在编号为 i i i 的正三角形内的三个顶点编号为 ( 4 i + 2 , A ) , ( 4 i + 2 , B ) , ( 4 i + 2 , C ) (4i+2,A),(4i+2,B),(4i+2,C) ( 4 i + 2 , A ) , ( 4 i + 2 , B ) , ( 4 i + 2 , C ) 。特别地,规定大三角形的三个顶点编号为 ( 1 , A ) , ( 1 , B ) , ( 1 , C ) (1,A),(1,B),(1,C) ( 1 , A ) , ( 1 , B ) , ( 1 , C ) 。
接下来是如何求出一个三角形所代表的顶点编号。我们可以模拟 extend 的过程,维护 extend 每一层对应三角形的信息。比如,要查询 △ 25 \triangle25 △25 ,我们模拟 extend 的过程是 { △ 1 , △ 6 , △ 25 } \{\triangle1,\triangle6,\triangle25\} { △1 , △6 , △25 } 。扩展到下一层显然要知道当前三角形的三个顶点编号。问题是在扩展到下一层时如何求出下一层三角形的三个顶点编号。由于顶点编号与三角形为正三角形还是倒三角形有关,那么我们根据当前三角形 △ i \triangle i △ i 和下一层三角形 △ 4 i + k ( k ∈ { 0 , 1 , 2 , 3 } ) \triangle 4i+k(k\in\{0,1,2,3\}) △4 i + k ( k ∈ { 0 , 1 , 2 , 3 }) 是正三角形还是倒三角形分类讨论:
为了方便,本章节将 △ i \triangle i △ i 相邻的大小相同的最多三个三角形(如果有)称作 △ i \triangle i △ i 的邻域三角形 。
当 △ i \triangle i △ i 和 △ 4 i + k \triangle 4i+k △4 i + k 均为正三角形时,△ 4 i + k \triangle 4i+k △4 i + k 的三个顶点为 △ i \triangle i △ i 的一个顶点和 △ 4 i + 2 \triangle 4i+2 △4 i + 2 的两个顶点(即编号为 4 i + 2 4i+2 4 i + 2 的三个顶点其中之二);
当 △ i \triangle i △ i 为正三角形且 △ 4 i + k \triangle 4i+k △4 i + k 为倒三角形时,k = 2 k=2 k = 2 (恰好编号为 4 i + 2 4i+2 4 i + 2 的三个顶点);
当 △ i \triangle i △ i 为倒三角形且 △ 4 i + k \triangle 4i+k △4 i + k 为正三角形时,k = 2 k=2 k = 2 ,三个顶点为 △ i \triangle i △ i 的三个邻域三角形 △ j \triangle j △ j 的分别某条边的中点,即 ( 4 j + 2 , α ) (4j+2,\alpha) ( 4 j + 2 , α ) ;
当 △ i \triangle i △ i 和 △ 4 i + k \triangle 4i+k △4 i + k 均为倒三角形时, 的三个顶点为 △ i \triangle i △ i 的一个顶点和三个邻域三角形之二 △ j \triangle j △ j 的分别某条边的中点,即 ( 4 j + 2 , α ) (4j+2,\alpha) ( 4 j + 2 , α ) 。
然后我们会发现当且仅当 △ i \triangle i △ i 是倒三角形时,我们需要同时维护邻域三角形的编号。注意要将编号次关键字 A , B , C A,B,C A , B , C 的方向固定。
这里给出笔者的 get_id \text{get\_id} get_id 函数供大家参考。
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 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 vector<pi>get_id (int x){ vector<int >v; while (x>1 ){ v.push_back (x&3 ); x>>=2 ; } reverse (v.begin (),v.end ()); pi d0=pi (1 ,0 ),d1=pi (1 ,1 ),d3=pi (1 ,3 );int t0=0 ,t1=0 ,t3=0 ;bool flag=0 ; for (auto c:v){ int mid=(x<<2 )|2 ; if (c==0 ){ if (!flag){ d1=pi (mid,3 ); d3=pi (mid,1 ); t0=t1=t3=0 ; }else { d1=pi ((t3<<2 )|2 ,3 ); d3=pi ((t1<<2 )|2 ,1 ); t0=mid,t1=(t1<<2 )|3 ,t3=(t3<<2 )|1 ; } }else if (c==1 ){ if (!flag){ d0=pi (mid,3 ); d3=pi (mid,0 ); t0=t1=t3=0 ; }else { d0=pi ((t3<<2 )|2 ,3 ); d3=pi ((t0<<2 )|2 ,0 ); t0=(t0<<2 )|3 ,t1=mid,t3=(t3<<2 )|0 ; } }else if (c==2 ){ if (!flag){ d0=pi (mid,0 ); d1=pi (mid,1 ); d3=pi (mid,3 ); t0=(x<<2 )|0 ,t1=(x<<2 )|1 ,t3=(x<<2 )|3 ; }else { d0=pi ((t0<<2 )|2 ,0 ); d1=pi ((t1<<2 )|2 ,1 ); d3=pi ((t3<<2 )|2 ,3 ); t0=t1=t3=0 ; } }else if (c==3 ){ if (!flag){ d0=pi (mid,1 ); d1=pi (mid,0 ); t0=t1=t3=0 ; }else { d0=pi ((t1<<2 )|2 ,1 ); d1=pi ((t0<<2 )|2 ,0 ); t0=(t0<<2 )|1 ,t1=(t1<<2 )|0 ,t3=mid; } } flag^=c==2 ; x=(x<<2 )|c; } return {d0,d1,d3}; }
Magical Tiered Cake(CF2232D)
来源:CF Round 1101 D
难度:2000
汉诺塔问题变形。Ad - hoc。
先考虑什么时候是无解的。当存在 a i ≥ i a_i\ge i a i ≥ i 时,发现第 i i i 个盘子无论如何都动不了(因为在它自身能动之前上面永远小于 i i i 个盘子)。
特殊情况 ∀ i ∈ [ 1 , n ] , a i = 0 \forall i\in[1,n],a_i=0 ∀ i ∈ [ 1 , n ] , a i = 0 就是普通的汉诺塔问题。
接下来考虑一般情况,我们希望对于一般情况也能像汉诺塔问题一样递归处理。然后我们可以找到一样的递归动机,即最大的一个盘子移动的目标位置顶上不能有任何比它还大的盘子。那么就考虑最大的一个盘子什么时候是能动的。设此时需要处理有 k k k 个盘子的子问题,将所有盘子从 A 地移动到 C 地,B 地可以作为中转点。那么当 a k = 0 a_k=0 a k = 0 时,照原样汉诺塔问题,即需要把上面的 k − 1 k-1 k − 1 个盘子都移动到 B 地才能动第 k k k 个盘子。即操作:
s o l v e ( k − 1 , A → B ) , m o v e ( k , A , C ) , s o l v e ( k − 1 , B → C ) solve(k-1,A\to B),move(k,A,C),solve(k-1,B\to C)
so l v e ( k − 1 , A → B ) , m o v e ( k , A , C ) , so l v e ( k − 1 , B → C )
。当 a k > 0 a_k\gt 0 a k > 0 时,有一个比较简单直接的方法就是先把最上面的 k − 1 − a k k-1-a_k k − 1 − a k 个盘子先移到 B,然后从 A 移第 k k k 个盘子到 C,再把 B 的 k − 1 − a k k-1-a_k k − 1 − a k 个盘子移回 A,最后把还在 A 的 k − 1 k-1 k − 1 个盘子移到 C。即操作:
s o l v e ( k − 1 − a k , A → B ) , m o v e ( k , A , C ) , s o l v e ( k − 1 − a k , B → A ) , s o l v e ( k − 1 , A → C ) solve(k-1-a_k,A\to B),move(k,A,C),solve(k-1-a_k,B\to A),solve(k-1,A\to C)
so l v e ( k − 1 − a k , A → B ) , m o v e ( k , A , C ) , so l v e ( k − 1 − a k , B → A ) , so l v e ( k − 1 , A → C )
。不难证明这样的步数一定在 2 k 2^k 2 k 以内。
Two Arithmetic Progressions
来源:AT_arc221_a
难度:1686
我们发现,由 gcd ( a , b ) = gcd ( b , a m o d b ) \gcd(a,b)=\gcd(b,a\bmod b) g cd( a , b ) = g cd( b , a mod b ) 可以对 A A A 和 C C C 进行消元。消到 C C C 为零,也就是形如求 gcd ( A i + B , D ) \gcd(Ai+B,D) g cd( A i + B , D ) 的形态。
接下来想一想如何求出 gcd ( A i + B , D ) \gcd(Ai+B,D) g cd( A i + B , D ) 。当 D D D 是负数是,取正即可。为零的情况放到章末再讨论。可以发现,我们通过枚举 D D D 的约数 d d d ,对每一个约数判断是否能够整除 A i + B Ai+B A i + B ,然后再稍微容斥一下可以求出 gcd \gcd g cd 的和。所以现在我们要快速求出所有 A i + B Ai+B A i + B 被 d d d 整除的个数。我们发现对于 i i i 满足 d ∣ A i + B d\mid Ai+B d ∣ A i + B 的解集必然是一个无限等差数列,所以我们只要找到最小的 i i i 就行。
对于 d ∣ A i + B d\mid Ai+B d ∣ A i + B 可以转换为 − A i + d y = B -Ai+dy=B − A i + d y = B ,也就是解一个同余方程。这里我们可以用 exgcd 解决。
如果有误,或者更好的解法,欢迎私信骚扰。