压表的技巧。
ZJ一下:
T1,考试不会哈夫曼树只压到$1MB$最后截掉了一部分。
T2,直接暴力丢上去。$Theta(N+sqrt{N}log N)$
T3,现场码出左右旋然后就不会了$QAQ$
TJ一下:
T1
先讲讲考试打的是什么表。
首先码了一个$Dijkstra$,然后它跑的还挺快,$3s$可以直接打出全部的答案。(比跑不出来的暴力好不少……)
于是把表打出来,因为我考虑到16进制的数可能短于10进制。于是使用了$0x$。
发现……$3MB$。
我……然后顺手打了一下10进制。……$2MB$(数值足够小的时候前导会大量冗余)
那么又看了一眼最大值,48……进一步压表,每一个数就可以用1个字。
最后$1MB$
后记:
——极限压表。
先摆上压之前的,长这个样:
记0:
为了方便压表,将原序列转化成前缀和形式。
从48种字符处理成11种,
便于下面的压表。
记1:
使用相等字符压表。压缩效果并不好……实测无效=.=
效果:
不使用前缀和优化:
××我要前缀和有何用
下面有用(滑稽
附:前缀和后相等的串长会全部减一,于是就会导致连续相等字符数减少于是前缀和压缩效果会变差
记2:
处理成前缀和后,可以将两个字符暴力压成一个……
$11$进制,两位是$121$,一个$char$是$127$,挺好。
但是问题在于在$mathsf{ASCII}$表码上找不到一段长度$geq 121$的有效字符?
下面是打出来的表码
0 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 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W 88 X 89 Y 90 Z 91 [ 92 93 ] 94 ^ 95 _ 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w 120 x 121 y 122 z 123 { 124 | 125 } 126 ~是这样……
所以实现起来可能还需要一些其他的技巧?
理论上可以实现并压到$500KB$
记3:
一种专业压缩的数据结构:哈夫曼树。
显然我去颓了一波代码。
并且实现也像××一样。
在前缀和后,内部的字符频数是这样的:
G H I J K L M N O P Q 407022 326991 169760 63867 23165 6733 1822 486 119 30 5 那么在这棵树上是长这个样的:(这里我们用H表示0)
编号 字母 字符频数 编码 1 G 407022 0 2 H 326991 11 3 I 169760 101 4 J 63867 1001 5 K 23165 10001 6 L 6733 100001 7 M 1822 1000001 8 N 486 10000001 9 O 119 100000001 10 P 30 1000000001 11 Q 5 1000000000 具体节点:
编号 字符 权值 父亲 左孩子 右孩子 1 G 407022 21 0 0 2 H 326991 20 0 0 3 I 169760 19 0 0 4 J 63867 18 0 0 5 K 23165 17 0 0 6 L 6733 16 0 0 7 M 1822 15 0 0 8 N 486 14 0 0 9 O 119 13 0 0 10 P 30 12 0 0 11 Q 5 12 0 0 12 35 13 11 10 13 154 14 12 9 14 640 15 13 8 15 2462 16 14 7 16 9195 17 15 6 17 32360 18 16 5 18 96227 19 17 4 19 265987 20 18 3 20 592978 21 19 2 21 1000000 0 1 20 :中间节点,具体字符无影响。
那么理论压缩后表长:$244KB$
优秀的算法!
依然过不了……但是可以启发思路(巨雾)
部分内容源自ooo大神
$$ ext{%%%ooo}$$
建边(实际不需要建),跑$mathsf{SPFA}$,从$i$向$i-1$和$ki$跑。
打表验证$2 leq k leq 7$即可
用$dij$会比较卡常。
因为这道题的特点,图比较稀疏并且有比较强的拓扑性,于是$mathsf{SPFA}$可行。
#include <iostream> #include <cstring> #include <cstdio> #include <queue> #define N 11111111 #define re register using namespace std; typedef pair<int,int> pii; struct Myqueue{ int A[N*3],f,b; void clear(){f=b=0;} bool empty(){return f==b;} void push(const int k){A[b++]=k;} void pop(){f++;} int front(){return A[f];} }; Myqueue q; char dis[N],is_v[N]; const char mx[]={2,3,5,7}; void spfa(){ memset(dis,0x3f,sizeof dis); dis[1]=0; q.push(1); is_v[1]=1; while(!q.empty()){ int f=q.front();q.pop(); if(f-1>=0 && dis[f-1]>dis[f]+1){ dis[f-1]=dis[f]+1; if(!is_v[f-1]){ q.push(f-1); is_v[f-1]=1; } } if(f!=0){ for(re int i=2;i<=7&&f*i<=1000000;++i){ if(dis[i*f]>dis[f]+i){ dis[i*f]=dis[f]+i; if(!is_v[i*f]){ q.push(i*f); is_v[i*f]=1; } } } } is_v[f]=0; } } int main(){ int v; spfa(); cin>>v; cout<<int(dis[v])<<endl; // cout<<clock()<<endl; }
T2
杜教筛
T3
不会