19-10-16-Night-D

压表的技巧。

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大神

$$\text{%%%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

不会

猜你喜欢

转载自www.cnblogs.com/kalginamiemeng/p/Exam20191016-Night.html
D16