# Tsinghua 2018 DSA PA2简要题解

upd：任务倒是完成了，我也自闭了。

CST2018 2-1 Meteorites：

乘法版的石子合并，堆 + 高精度。

写起来有点烦貌似。

upd：由于内存问题我高精度是动态开点，同时用的是可并堆（比较简单）。

  1 #include <cstdio>
2 #include <cmath>
3 #include <cstring>
4
5 using namespace std;
6 typedef double lf;
7 typedef long long ll;
8 const lf pi = acos(-1.0);
9 const int N = 200005;
10 const int MaxLen = 2000005;
11 const int MOD = 10000;
12
13 struct BigNumber;
14 inline int getint();
15 inline void getInt(BigNumber &res); // get Big number
16 inline void print(ll *num, int Len);
17
18 template<class T> inline void swap(T &x, T &y) {
19     register T tmp;
20     tmp = x, x = y, y = tmp;
21 }
22
23 int n;
24
25 struct Complex {
26     lf x, y;
27
28     Complex(lf _x = 0, lf _y = 0) : x(_x), y(_y) {
29     }
30     ~Complex() {
31     }
32
33     Complex operator +(const Complex &_c) {
34         return Complex(x + _c.x, y + _c.y);
35     }
36     Complex operator -(const Complex &_c) {
37         return Complex(x - _c.x, y - _c.y);
38     }
39     Complex operator *(const Complex &_c) {
40         return Complex(x * _c.x - y * _c.y, x * _c.y + y * _c.x);
41     }
42     Complex operator *(const lf &_x) {
43         return Complex(_x * x, _x * y);
44     }
45     Complex operator !() {
46         return Complex(x, -y);
47     }
48 } w[MaxLen], A[MaxLen], B[MaxLen], C[MaxLen];
49
50 struct BigNumber {
51     ll *x;
52     int len;
53
54     BigNumber() {
55         x = new ll[1];
56         x[0] = 0;
57         len = 0;
58     }
59
60     void destroy() {
61         if (x) delete[] x;
62     }
63
64     ~BigNumber() {
65         destroy();
66     }
67
68     void ChangeSize(int length) {
69         if (x) delete[] x;
70         x = new ll[length + 1];
71         memset(x, 0, (length + 1) * sizeof(x));
72         len = 0;
73     }
74
75     void zero() {
76         ChangeSize(0);
77         x[0] = 0;
78         len = 0;
79     }
80     void one() {
81         ChangeSize(0);
82         x[0] = 1;
83         len = 0;
84     }
85
86     void copy(const BigNumber &_num) {
87         ChangeSize(_num.len);
88         len = _num.len;
89         for (int i = len; i >= 0; --i)
90             x[i] = _num.x[i];
91     }
92
93     inline bool operator > (const BigNumber &_num) const {
94         if (len > _num.len) return 1;
95         if (len < _num.len) return 0;
96         for (int i = len; i >= 0; --i) {
97             if (x[i] > _num.x[i]) return 1;
98             if (x[i] < _num.x[i]) return 0;
99         }
100         return 1;
101     }
102
103     void print() {
104         for (int i = len; i >= 0; --i)
105             printf(i == len ? "%lld" : "%04lld", x[i]);
106         puts("");
107     }
108 };
109
110 struct heap {
111     heap *ls, *rs;
112     BigNumber num;
113     int dep;
114
115     void *operator new(size_t) {
116         static heap mempool[N << 2], *c = mempool;
117         c -> ls = c -> rs = NULL;
118         c -> dep = 1;
119         c -> num.zero();
120         return c++;
121     }
122
123     inline void get_value(const BigNumber &_num) {
124         num.copy(_num);
125     }
126
127     #define Dep(p) (p ? p -> dep : 0)
128     inline void update() {
129         dep = Dep(rs) + 1;
130     }
131
132     friend heap* merge(heap *x, heap *y) {
133         if (!x) return y;
134         if (!y) return x;
135         if (x -> num > y -> num) swap(x, y);
136         x -> rs = merge(x -> rs, y);
137         if (Dep(x -> rs) > Dep(x -> ls))
138             swap(x -> ls, x -> rs);
139         x -> update();
140         return x;
141     }
142     #undef Dep
143
144     inline heap* pop() {
145         this -> num.ChangeSize(0);
146         return merge(ls, rs);
147     }
148 } *Root;
149
150
151 void work(const BigNumber &a, const BigNumber &b, BigNumber &c) {
152     int n = a.len, m = b.len, l = a.len + b.len + 4;
153     c.ChangeSize(l);
154
155     for (int k = 0; k <= l; ++k) c.x[k] = 0;
156
157     for (int i = 0; i <= n; ++i)
158         for (int j = 0; j <= m; ++j)
159             c.x[i + j] += a.x[i] * b.x[j];
160     for (int k = 0; k < l; ++k) {
161         c.x[k + 1] += c.x[k] / MOD;
162         c.x[k] %= MOD;
163     }
164
165     while (c.x[l] == 0) --l;
166     c.len = l;
167 }
168
169 int main() {
170     BigNumber ans, tmp, a, b, c;
171     heap *tmp_node;
172
173     n = getint();
174     tmp.one();
175
176     Root = new()heap;
177     Root -> get_value(tmp);
178
179     for (int i = 1; i <= n; ++i) {
180         getInt(tmp);
181         tmp_node = new()heap;
182         tmp_node -> get_value(tmp);
183
184         Root = merge(Root, tmp_node);
185     }
186
187
188     ans.one();
189     for (int i = 1; i <= n; ++i) {
190         a.copy(Root -> num);
191         Root = Root -> pop();
192         b.copy(Root -> num);
193         Root = Root -> pop();
194         /*
195         puts("a and b:");
196         a.print();
197         b.print();
198         puts("---------");
199         */
200         work(a, b, c);
201         /*
202         puts("c:");
203         c.print();
204         */
205         tmp.copy(ans);
206         if (i != 1) {
207             /*
208             puts("----");
209             tmp.print();
210             c.print();
211             */
212             work(tmp, c, ans);
213         }
214         /*
215         puts("ans:");
216         ans.print();
217         */
218         tmp_node = new()heap;
219         tmp_node -> get_value(c);
220
221         Root = merge(Root, tmp_node);
222     }
223     ans.print();
224
225     return 0;
226 }
227
228 const int BUF_SIZE = 30;
229 char buf[BUF_SIZE], *buf_s = buf, *buf_t = buf + 1;
230 #define isdigit(x) ('0' <= x && x <= '9')
231 #define PTR_NEXT() { \
232     if (++buf_s == buf_t) \
233         buf_s = buf, buf_t = buf + fread(buf, 1, BUF_SIZE, stdin); \
234 }
235
236 int getint() {
237     register int x = 0;
238     while (!isdigit(*buf_s)) PTR_NEXT();
239     while (isdigit(*buf_s)) {
240         x = x * 10 + *buf_s - '0';
241         PTR_NEXT();
242     }
243     return x;
244 }
245
246 void getInt(BigNumber &res) {
247     static ll num[50];
248     static int len, Len, cnt, base;
249     memset(num, 0, sizeof(num)), len = 0;
250     for (int i = 0; i < 50; ++i) num[i] = 0;
251
252     while (!isdigit(*buf_s)) PTR_NEXT();
253     while (isdigit(*buf_s) && buf_s != buf_t) {
254         num[len++] = *buf_s - '0';
255         PTR_NEXT();
256     }
257     len -= 1;
258
259     res.ChangeSize((len + 4) / 4 - 1);
260     Len = -1, cnt = 0;
261     for (int i = len; i >= 0; --i) {
262         cnt++;
263         if (cnt % 4 == 1) Len += 1, base = 1;
264         res.x[Len] += base * num[i];
265         base *= 10;
266     }
267     res.len = Len;
268
269     /*
270     for (int i = 0; i <= len; ++i)
271         printf("%lld ", num[i]);
272     puts("");
273
274     res.print();
275     */
276 }
View Code

CST2018 2-2 Circuit

首先是一个二进制trie + 贪心，但是由于有间隔限制，所以这个trie要支持插入和删除标记操作。

写起来并不麻烦。

upd：哪个xx卡内存。。。要把指针改成数组下标，同时把叶子和非叶子分开。

  1 #include <cstdio>
2 #include <cstring>
3
4 using namespace std;
5 typedef unsigned long long ull;
6 const int N = 5e5 + 5;
7 const int SIZE = 18e6 + 5;
8 int n, k;
9 int a[100];
10 ull f[N];
11
12 struct trie_node {
13     int son[2];
14     int cnt;
15 } trie[SIZE];
16 int Root;
17 int cnt = 0;
18
19 struct leaf_node {
20     int cnt;
21     int next;
22 } leaf[N];
23 int cnt_leaf = 0;
24
25
26 int get_new() {
27     ++cnt;
28     trie[cnt].son[0] = trie[cnt].son[1] = -1;
29     trie[cnt].cnt = 0;
30     return cnt;
31 }
32
33 int get_new_leaf() {
34     ++cnt_leaf;
35     leaf[cnt_leaf].cnt = 0;
36     leaf[cnt_leaf].next = -1;
37     return cnt_leaf;
38 }
39
40 struct Queue {
41     int next, value;
42 } q[N];
43 int size;
44
45 void init() {
46     Root = get_new();
47 }
48
49 void trie_add(int *a, int id) {
50     int tmp = Root;
51     for (int i = 1; i <= 64; ++i) {
52         trie[tmp].cnt += 1;
53         if (trie[tmp].son[a[i]] == -1) {
54             if (i != 64) trie[tmp].son[a[i]] = get_new();
55             else trie[tmp].son[a[i]] = get_new_leaf();
56         }
57         tmp = trie[tmp].son[a[i]];
58     }
59
60     //leaf
61     leaf[tmp].cnt += 1;
62     int t = leaf[tmp].next;
63     if (t == -1) {
64         size += 1;
65         leaf[tmp].next = size;
66         q[size].next = -1;
67         q[size].value = id;
68     } else {
69         while (q[t].next != -1) t = q[t].next;
70         size += 1;
71         q[t].next = size;
72         q[size].next = -1;
73         q[size].value = id;
74     }
75 }
76
77 void trie_delete(int *a) {
78     int tmp = Root;
79     for (int i = 1; i <= 64; ++i) {
80         trie[tmp].cnt -= 1;
81         tmp = trie[tmp].son[a[i]];
82     }
83
84     //leaf
85     leaf[tmp].cnt -= 1;
86     leaf[tmp].next = q[leaf[tmp].next].next;
87 }
88
89 int trie_find(int *a, int id) {
90     int tmp = Root;
91     int t;
92     for (int i = 1; i <= 64; ++i) {
93         if (i != 64) {
94             t = trie[tmp].son[!a[i]];
95             if (t != -1 && trie[t].cnt != 0)
96                 tmp = trie[tmp].son[!a[i]];
97             else
98                 tmp = trie[tmp].son[a[i]];
99         } else {
100             t = trie[tmp].son[!a[i]];
101             if (t != -1 && leaf[t].cnt != 0)
102                 tmp = trie[tmp].son[!a[i]];
103             else
104                 tmp = trie[tmp].son[a[i]];
105         }
106     }
107     int x = leaf[tmp].next;
108     while (q[x].value == id) {
109         x = q[x].next;
110     }
111     return q[x].value;
112 }
113
114 //--------------------------------------------------------------------
115 //--------------------------------------------------------------------
116
117 void split(ull x) {
118     memset(a, 0, sizeof(a));
119     for (int i = 64; x; x >>= 1, --i) {
120         a[i] = x % 2;
121     }
122     /*
123     for (int i = 1; i <= 64; ++i)
124         printf("%d", a[i]);
125     puts("");
126     */
127 }
128
130     if (i < 0 || i > n) return;
131     //printf("Add    : %d %llu\n", i, f[i]);
132     split(f[i]);
134 }
135
136 void delete_in_trie(int i) {
137     if (i < 0 || i > n) return;
138     //printf("Delete : %d %llu\n", i, f[i]);
139     split(f[i]);
140     trie_delete(a);
141 }
142
143 int get_ans(int i) {
144     //printf("Find   : %d %llu\n", i, f[i]);
145     split(f[i]);
146     return trie_find(a, i);
147 }
148
149
150 int main() {
151     ull x;
152     scanf("%d%d", &n, &k);
153     /*
154     for (int i = 1; i <= n; ++i) {
155         scanf("%llu", &x);
156         printf("%llu\n", x);
157         f[i] = x;
158         split(x);
159     }
160     */
161
162     int L, R, nowL, nowR;
163     nowL = 1, nowR = 0;
164     L = 1 - k - 1;
165     R = 1 + k + 1;
166     init();
167     for (int i = 1; i <= n; ++i) {
169         while (nowR < R) {
170             nowR += 1;
171             scanf("%llu", &f[nowR]);
172             split(f[nowR]);
174         }
175         //delete in trie
176         while (nowL < L) {
177             delete_in_trie(nowL);
178             nowL += 1;
179         }
180
181         //printf("%d %d %d\n", i, nowL, nowR);
182         printf("%d\n", get_ans(i) - 1);
183
184         //change the segment
185         L += 1;
186         R += 1;
187         if (L > n) L = n;
188         if (R > n) R = n;
189     }
190
191     return 0;
192 }
View Code

CST2018 2-3-1 Mooney(basic)

求一个有向图的强连通分量个数，直接tarjan就好了。

写起来 = 抄板子。

upd：没写。

CST2018 2-3-2 Mooney

两个小问题：

第一个就是求最短路，Dijkstra。

第二个先按照前一道题tarjan求强连通分量，然后对DAG树形DP。

写起来有点麻烦。

upd：第一问我写了SLF优化的spfa，应该不卡，第二问要注意有的连通分量走不到T要删掉。

  1 #include <cstdio>
2
3 using namespace std;
4 const int N = 5e5 + 5;
5 const int M = 12e5 + 5;
6 const int inf = 1e9;
7
8
9 int n, m;
10 int f[N];
11 int tot, first[N];
12 int TOT, FIRST[N];
13
14 struct edge {
15     int next, to;
16     edge() {}
17     edge(int _n, int _t) : next(_n), to(_t) {}
18 } e[M], E[M];
19
20 inline void add_edge(int x, int y) {
21     e[++tot] = edge(first[x], y), first[x] = tot;
22 }
23
24 int q[N], v[N], dis[N];
25 void spfa(int S) {
26     int p, x, y, l, r;
27
28     for (x = 1; x <= n; ++x)
29         dis[x] = inf;
30
31     q[0] = S, dis[S] = (f[S] == 0), v[S] = 1;
32     for (l = r = 0; l != (r + 1) % N; ) {
33         p = q[l], ++l %= N;
34         for (x = first[p]; x; x = e[x].next) {
35             y = e[x].to;
36             if (dis[p] + (f[y] == 0) < dis[y]) {
37                 dis[y] = dis[p] + (f[y] == 0);
38                 if (!v[y]) {
39                     v[y] = 1;
40                     if (dis[y] < dis[q[l]]) q[(l += N - 1) %= N] = y;
41                     else q[++r %= N] = y;
42                 }
43             }
44         }
45         v[p] = 0;
46     }
47 }
48
49 int __main1__() {
50     spfa(1);
51     printf("%d\n", dis[n]);
52 }
53
54
55 int low[N], dfn[N], vis[N], inq[N], s[N], w[N];
56 int Cnt[N], num;
57 int VIS[N], ans[N];
58 int cnt, top;
59 int S, T;
60
61 inline int min(int x, int y) {
62     return x < y ? x : y;
63 }
64
65 inline int max(int x, int y) {
66     return x > y ? x : y;
67 }
68
69
70 void dfs(int p){
71     low[p] = dfn[p] = ++cnt;
72     vis[p] = inq[p] = 1;
73     s[++top] = p;
74     int x, y;
75     for (x = first[p]; x; x = e[x].next)
76         if (!vis[(y = e[x].to)]){
77             dfs(y);
78             low[p] = min(low[p], low[y]);
79         }else
80         if (inq[y]) low[p] = min(low[p], dfn[y]);
81     if (low[p] == dfn[p]){
82         ++num, y = 0;
83         while (y != p){
84             inq[(y = s[top--])] = 0;
85             w[y] = num;
86             if (f[y] == 1)
87                 ++Cnt[num];
88         }
89     }
90 }
91
92 inline void ADD_EDGE(int x, int y) {
93     E[++TOT] = edge(FIRST[x], y), FIRST[x] = TOT;
94 }
95
96 void rebuild_graph() {
97     int x, y;
98     for (int i = 1; i <= n; ++i)
99         for (x = first[i]; x; x = e[x].next)
100             if (w[i] != w[(y = e[x].to)])
102 }
103
104 int tag[N];
105
106 void work(int p) {
107     int x, y;
108     ans[p] = 0;
109     if (p == T) {
110         tag[p] = 1;
111         ans[p] = Cnt[T];
112         return;
113     }
114     for (x = FIRST[p]; x; x = E[x].next) {
115         if (!VIS[y = E[x].to]) {
116             VIS[y] = 1;
117             work(y);
118         }
119         if (tag[y]) tag[p] = 1;
120         ans[p] = max(ans[p], ans[y]);
121     }
122     if (tag[p] == 1)
123         ans[p] += Cnt[p];
124 }
125
126 int __main2__() {
127     for (int i = 1; i <= n; ++i)
128         if (!vis[i])
129             dfs(i);
130     rebuild_graph();
131
132     S = w[1];
133     T = w[n];
134     work(S);
135
136     printf("%d\n", ans[S]);
137 }
138
139 int main() {
140     int x, y;
141     char ch;
142
143     scanf("%d%d\n", &n, &m);
144     for (int i = 1; i <= n; ++i) {
145         ch = getchar();
146         while (ch != 'm' && ch != 'M')
147             ch = getchar();
148         if (ch == 'M') f[i] = 0;
149         if (ch == 'm') f[i] = 1;
150     }
151
152     for (int i = 1; i <= m; ++i) {
153         scanf("%d %d", &x, &y);
154         add_edge(x + 1, y + 1);
155     }
156
157     __main1__();
158     __main2__();
159
160     return 0;
161 }
View Code

CST2018 2-4 Sort

我记得好像是原题，忘了咋做了。

大概想法是归并排序的时候分析归并子序列1的前两个数和子序列2的前一个数的大小，不能证明其上界。。。

写起来不麻烦。。就是不知道对不对。

upd：自闭了，四路归并写不来。

CST2018 2-5 ChromPoly

也没有什么想法，直接dfs好像就可以了。

问题是怎么判断两张图是同构的，这样子可以大幅度剪枝，甚至在小情况的时候进行打表预处理。

然后这是个NP问题，我xxxx。

我还是决定用hash来做。。但是还是没想好怎么hash

写起来不麻烦，如果hash搞定了的话。

upd：并不是hash，有别人的代码，戳这里。写到一半自闭了，不想写了。

CST2018 2-6 Temperature

二维数组，单点修改，矩阵求和，在线算法。

好像可以写二维的树状数组，子矩阵可以通过左上角二维前缀和解决。

写起来比较简单。

upd：好像没什么好讲的，要是每道题都这么简单就好了。

 1 #include <cstdlib>
2 #include "temperature.h"
3
4 using namespace std;
5 const int N = 1205;
6 const int M = 1205;
7
8 int sum[N][M];
9 int _t[N][M];
10
11 void change(int, int, int);
12
13 void init(int n, int m, int **temp) {
14     for (int i = 1; i <= n; ++i)
15         for (int j = 1; j <= m; ++j) {
16             change(i, j, temp[i][j]);
17             _t[i][j] = temp[i][j];
18         }
19 }
20
21 inline int Q(int x, int y) {
22     int res = 0;
23     for (int i = x; i; i -= i&(-i))
24         for (int j = y; j; j -= j&(-j))
25             res += sum[i][j];
26     return res;
27 }
28
29 int query(int x1, int y1, int x2, int y2) {
30     return (Q(x2, y2) - Q(x2, y1 - 1) - Q(x1 - 1, y2) + Q(x1 - 1, y1 - 1)) / ((x2 - x1 + 1) * (y2 - y1 + 1));
31 }
32
33 void change(int x, int y, int temp) {
34     int del = temp - _t[x][y];
35     _t[x][y] = temp;
36     for (int i = x; i < N; i += i&(-i))
37         for (int j = y; j < M; j += j&(-j))
38             sum[i][j] += del;
39 }
View Code

CST2018 2-7 Virus

就是求所有离0最远的1，直接bfs就好了。

写起来非常简单。

upd： 好像也没什么好讲的，但是少了5分。。。有毒。

 1 #include <cstdio>
2
3 using namespace std;
4 const int N = 1005;
5 const int M = 1005;
6 const int CNT = N * M;
7
8 const int dx[] = {1, -1, 0, 0};
9 const int dy[] = {0, 0, 1, -1};
10
11 int ans;
12 int n, m;
13 int w[N][M];
14 int x[CNT], y[CNT];
15 int time[CNT], vis[CNT];
16 int q[CNT], l, r;
17 int cnt, cnt_virus;
18
19
20 inline void _max(int &x, int y) {
21     if (x < y) x = y;
22 }
23
24 inline void push_q(int x, int y, int t) {
25     r++;
26     q[r] = w[x][y];
27     vis[w[x][y]] = 1;
28     time[w[x][y]] = t;
29     ans += t;
30     ++cnt_virus;
31 }
32
33 inline bool IN(int x, int y) {
34     return 0 < x && x <= n && 0 < y && y <= m;
35 }
36
37 int main() {
38     char ch;
39     int C;
40     int _x, _y, __x, __y;
41     scanf("%d%d", &n, &m);
42     cnt = 0;
43     l = 1, r = 0;
44     for (int i = 1; i <= n; ++i)
45         for (int j = 1; j <= m; ++j) {
46             w[i][j] = ++cnt;
47             x[cnt] = i, y[cnt] = j;
48
49             ch = getchar();
50             while (ch != '0' && ch != '1') ch = getchar();
51             if (ch == '0') push_q(i, j, 0);
52         }
53     while (l <= r) {
54         C = q[l];
55         _x = x[C], _y = y[C];
56         for (int i = 0; i < 4; ++i) {
57             __x = _x + dx[i], __y = _y + dy[i];
58             if (IN(__x, __y) && !vis[w[__x][__y]])
59                 push_q(__x, __y, time[w[_x][_y]] + 1);
60         }
61         ++l;
62     }
63     printf("%d\n", ans);
64     return 0;
65 }
View Code

CST2018 2-8 MST

求最小生成树。

ctrl c + ctrl v = AC。

20分题：1，2，3-2，4， 5， 6。

15分题：3-1， 7， 8。

By Xs酱~ 转载请说明 博客地址：http://www.cnblogs.com/rausen
posted on 2018-11-27 21:56  Xs酱~  阅读(440)  评论(0编辑  收藏  举报