前天深夜,数据结构课程再次留下了小作业。本着在DDL面前愉悦身心的态度,我用两天的时间水完了它们。这里来挂一下十分迷惑的思路和奇奇怪怪的代码。
注意:本篇博文主要面对本班同学,所以我不会在此提供题面、数据范围等信息。如果你没有相关内容说明你不是本篇博客的对象,请自行将鼠标移到页面选单点击图标「x」。
抄代码的就无所谓了,反正我猜我写的像...一样的代码也没有人愿意抄。
3020:
哈夫曼树板题,实现二叉堆即可。
注意每次减少m - 1个元素,所以要补充元素个数到(n - 1) mod (m - 1) = 1,特判m = 2的情况,此时不用补。
代码:
(二叉堆都写炸了,丢~人!)
#include <iostream> #include <cstdio> using namespace std; const int maxn = 2e5 + 1e2; struct Heap { long long dat[maxn]; int n; void fixHeapDown() { int pos = 1; while(pos <= n) { const int ls = pos << 1, rs = pos << 1 | 1; if(ls > n) return; if(rs > n) { if(dat[pos] < dat[ls]) return; swap(dat[pos], dat[ls]), pos = ls; } else { if(dat[pos] <= min(dat[ls], dat[rs])) return; if(dat[ls] <= dat[rs]) swap(dat[pos], dat[ls]), pos = ls; else swap(dat[pos], dat[rs]), pos = rs; } } } void fixHeapUp() { int pos = n; while(pos != 1) if(dat[pos] < dat[pos >> 1]) swap(dat[pos], dat[pos >> 1]), pos >>= 1; else break; } void push(long long x) { dat[++n] = x; fixHeapUp(); } long long pop() { int ret = dat[1]; dat[1] = dat[n--]; fixHeapDown(); return ret; } int size() { return n; } }hp; int main() { static int n, m; long long ans = 0, su; scanf("%d%d", &n, &m); for(int i = 1, x; i <= n; i++) scanf("%d", &x), hp.push(x); while(m != 2 && hp.size() % (m - 1) != 1) hp.push(0); while(hp.size() != 1) { su = 0; for(int i = 1; i <= m; i++) su += hp.pop(); ans += su, hp.push(su); } printf("%lld\n", ans); return 0; }
1358:
类似找点分治中找重心的操作,只不过要你找出所有分割后子块大小小于一半的点。
搜索一遍算出大小,最大的块大小就是max(子树最大大小, n - 该点子树大小)。
代码:
#include <iostream> #include <cstdio> using namespace std; const int maxn = 2e5 + 1e2; int s[maxn], t[maxn << 1], nxt[maxn << 1], siz[maxn], mxs[maxn], n, cnt; inline void coredge(int from, int to) { t[++cnt] = to, nxt[cnt] = s[from], s[from] = cnt; } inline void addedge(int a, int b) { coredge(a, b), coredge(b, a); } inline void dfs(int pos, int fa) { siz[pos] = 1; for(int at = s[pos]; at; at = nxt[at]) if(t[at] != fa) dfs(t[at], pos), siz[pos] += siz[t[at]], mxs[pos] = max(mxs[pos], siz[t[at]]); mxs[pos] = max(mxs[pos], n - siz[pos]); } int main() { scanf("%d", &n); for(int i = 1, a, b; i < n; i++) scanf("%d%d", &a, &b), addedge(a, b); dfs(1, -1); for(int i = 1; i <= n; i++) if(mxs[i] <= n / 2) printf("%d\n", i); return 0; }
1056:
并查集维护糖与盒子的关系,二叉堆维护盒子中的糖数。(因为p很小,所以可以暴力~)
如果一个堆中一个盒子被取出时发现其所在并查集的根不是自己,则说明它已经被合并掉了,直接丢弃即可。
代码:
#include <iostream> #include <cstdio> #define debug cerr using namespace std; const int maxn = 1e6 + 1e2; struct Node { int v, p; friend bool operator < (const Node &a, const Node &b) { return a.v > b.v; } friend bool operator <= (const Node &a, const Node &b) { return a.v >= b.v; } }; struct Heap { Node dat[maxn]; int n; void fixHeapDown() { int pos = 1; while(pos <= n) { const int ls = pos << 1, rs = pos << 1 | 1; if(ls > n) return; if(rs > n) { if(dat[pos] < dat[ls]) return; swap(dat[pos], dat[ls]), pos = ls; } else { if(dat[pos] <= min(dat[ls], dat[rs])) return; if(dat[ls] <= dat[rs]) swap(dat[pos], dat[ls]), pos = ls; else swap(dat[pos], dat[rs]), pos = rs; } } } void fixHeapUp() { int pos = n; while(pos != 1) if(dat[pos] < dat[pos >> 1]) swap(dat[pos], dat[pos >> 1]), pos >>= 1; else break; } void push(Node x) { dat[++n] = x; fixHeapUp(); } Node pop() { Node ret = dat[1]; dat[1] = dat[n--]; fixHeapDown(); return ret; } int size() { return n; } }hp; struct UnionFindSet { int fa[maxn]; int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); } }ufs; int siz[maxn]; int main() { static int n, m; static char o[1010]; static Node temp[1010]; scanf("%d%d", &n, &m); for(int i = 1; i <= n; i++) ufs.fa[i] = i, hp.push((Node){siz[i] = 1, i}); for(int i = 1, x, y; i <= m; i++) { scanf("%s%d", o, &x); if(*o == 'C') { scanf("%d", &y), x = ufs.find(x), y = ufs.find(y); if(x == 0 || y == 0 || x == y) continue; ufs.fa[x] = ufs.fa[y] = ++n, ufs.fa[n] = n, hp.push((Node){siz[n] = siz[x] + siz[y], n}); } else if(*o == 'D') { x = ufs.find(x); ufs.fa[x] = 0; } else { int top = 0; while(hp.size() && top < x) { Node x = hp.pop(); // debug << "v = " << x.v << " p = " << x.p << endl; if(ufs.find(x.p) == x.p) temp[++top] = x; } if(top < x) puts("0"); else printf("%d\n", temp[top].v); for(int i = 1; i <= top; i++) hp.push(temp[i]); } } return 0; }
4118:
树的不同形态是由c(m, 子树个数)产生的。
前序遍历、后序遍历中,相同字母所控制的一段连续区间一定为同一子树,例如:(a{...},{...}a),所以直接递归即可。
#include <cstdio> #include <cstring> typedef long long int lli; constexpr int maxn = 50; char sa[maxn], sb[maxn]; int ia[maxn][maxn], ib[maxn][maxn], rf[maxn], m, n; inline lli fac(int x) { lli ret = 1; while(x) ret *= x--; return ret; } inline lli c(int n, int m) { return fac(n) / (fac(m) * fac(n - m)); } lli dfs(int l, int r, int ll, int rr) { if(ia[l][r] != ib[ll][rr] || sa[l] != sb[rr]) return 0; // no solution. ++l, --rr; lli ret = 1; int su = 0; while(l <= r && ll <= rr) { int rangeRR = rf[l], rangeR = rangeRR - ll + l; ++su, ret *= dfs(l, rangeR, ll, rangeRR); l = rangeR + 1, ll = rangeRR + 1; } ret *= c(m, su); return ret; } int main() { scanf("%d%s%s", &m, sa + 1, sb + 1), n = strlen(sa + 1); for(int i = 1; i <= n; i++) for(int j = i; j <= n; j++) for(int k = i; k <= j; k++) { ia[i][j] |= (1 << (sa[k] - 'a')); ib[i][j] |= (1 << (sb[k] - 'a')); } for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) if(sa[i] == sb[j]) rf[i] = j; printf("%lld\n", dfs(1, n, 1, n)); return 0; }
4119:
枚举集合点,树剖lca求距离加和即可。
代码:
#include <cstdio> const int maxn = 2e4 + 1e2; int s[maxn], t[maxn << 1], nxt[maxn << 1], l[maxn << 1], fa[maxn], dep[maxn], dis[maxn], siz[maxn], son[maxn], top[maxn], cnt, n, ans = -1, ass = 0x3f3f3f3f, p[3]; inline void coredge(int from, int to, int len) { t[++cnt] = to, l[cnt] = len, nxt[cnt] = s[from], s[from] = cnt; } inline void addedge(int a, int b, int l) { coredge(a, b, l), coredge(b, a, l); } inline void dfs1(int pos) { dep[pos] = dep[fa[pos]] + (siz[pos] = 1); for(int at = s[pos]; at; at = nxt[at]) if(t[at] != fa[pos]) fa[t[at]] = pos, dis[t[at]] = dis[pos] + l[at], dfs1(t[at]), siz[pos] += siz[t[at]], son[pos] = siz[t[at]] > siz[son[pos]] ? t[at] : son[pos]; } inline void dfs2(int pos) { top[pos] = pos == son[fa[pos]] ? top[fa[pos]] : pos; for(int at = s[pos]; at; at = nxt[at]) if(t[at] != fa[pos]) dfs2(t[at]); } inline int lca(int x, int y) { while(top[x] != top[y]) dep[top[x]] > dep[top[y]] ? x = fa[top[x]] : y = fa[top[y]]; return dep[x] < dep[y] ? x : y; } inline int gdis(int x, int y) { return dis[x] + dis[y] - (dis[lca(x, y)] << 1); } int main() {scanf("%d", &n); for(int i = 0; i < 3; i++) scanf("%d", p + i); for(int i = 1, u, v, w; i < n; i++) scanf("%d%d%d", &u, &v, &w), addedge(u, v, w); dfs1(1), dfs2(1); for(int i = 1; i <= n; i++) {int s2 = 0; for(int j = 0; j < 3; j++) s2 += gdis(i, p[j]); if(s2 < ass) ans = i, ass = s2;} return printf("%d\n%d\n", ans, ass), 0; }
4120:
先缩点,考虑所有没有入度的块,它们一定要各问一次,设其数量为x,则答案就是1 - x / n。
注意如果存在一个块,它没有入度,大小为1,且连向的所有块都能被其他块指向,则这个块能用排除法解决。
实际上我不缩点也过了2333
代码:
#include <cstdio> const int maxn = 1e5 + 1e2, maxe = 3e5 + 1e2; int s[maxn], t[maxe], nxt[maxe], ind[maxn], cnt, siz; bool vis[maxn]; inline void addedge(int from, int to) { t[++cnt] = to, nxt[cnt] = s[from], s[from] = cnt; } inline void dfs(int pos) { if(vis[pos]) return; siz += (vis[pos] = 1); for(int at = s[pos]; at; at = nxt[at]) dfs(t[at]); } int main() { static int n, m, ans, flag; scanf("%d%d", &n, &m); for(int i = 1, a, b; i <= m; i++) scanf("%d%d", &a, &b), addedge(a, b), ++ind[b]; for(int i = 1; i <= n; i++) if(!ind[i]) siz = 0, dfs(i), flag += (siz == 1), ++ans; for(int i = 1; i <= n; i++) if(!vis[i]) siz = 0, dfs(i), flag += (siz == 1), ++ans; if(n != 1 && flag) --ans; printf("%0.6Lf\n", 1 - (long double)ans / n); return 0; }
4180:
有解说明给出的有向边一定构成DAG。拓扑排序,拓扑序小的点指向拓扑序大的点即可。正确性显然。(因为其他边起码不会反着指回来)
#include <iostream> #include <cstdio> using namespace std; const int maxn = 1e5 + 1e2; int s[maxn], t[maxn], nxt[maxn], deg[maxn], cnt; int u[maxn], v[maxn], id[maxn]; inline void addedge(int from, int to) { t[++cnt] = to, nxt[cnt] = s[from], s[from] = cnt; } struct Queue { int dat[maxn], st, ed; Queue() {st = 1, ed = 0;} void push(int x) {dat[++ed] = x;} int pop() {return dat[st++];} int size() {return ed - st + 1;} }que; int main() { static int n, p1, p2, iid; scanf("%d%d%d", &n, &p1, &p2); for(int i = 1, u, v; i <= p1; i++) scanf("%d%d", &u, &v), addedge(u, v), ++deg[v]; for(int i = 1; i <= p2; i++) scanf("%d%d", u + i, v + i); for(int i = 1; i <= n; i++) if(!deg[i]) que.push(i); while(que.size()) { const int pos = que.pop(); id[pos] = ++iid; for(int at = s[pos]; at; at = nxt[at]) if(!--deg[t[at]]) que.push(t[at]); } for(int i = 1; i <= p2; i++) printf("%d %d\n", id[u[i]] < id[v[i]] ? u[i] : v[i], id[u[i]] < id[v[i]] ? v[i] : u[i]); return 0; }
4122:
Tire树匹配下单词,然后二分答案,求包含所有可能出现元素的最小区间即可。
滑动窗口,维护计数,基础操作了。
注意n个单词中重复的只算一次。
代码:
(因为没有判0还WA了几发,丢~人!)
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int maxn = 1e5 + 1e2, maxe = 1e4 + 1e2; struct Tire { int ch[maxe][257], isEnd[maxe], cnt, root; Tire() {root = cnt = 1;}; void insert(char* s, int len, int id) { int cur = root; for(int i = 1; i <= len; i++) { if(!ch[cur][s[i]]) ch[cur][s[i]] = ++cnt; cur = ch[cur][s[i]]; } isEnd[cur] = id; } int find(char* s, int len) { int cur = root; for(int i = 1; i <= len; i++) cur = ch[cur][s[i]]; return isEnd[cur]; } }tr; int id[maxn], vis[maxe], fs, n; inline bool check(int len) { memset(vis, 0, sizeof vis); int siz = 0; for(int i = 1; i <= len; i++) if(id[i] && ++vis[id[i]] == 1) ++siz; if(siz == fs) return 1; for(int i = len + 1; i <= n; i++) { if(id[i] && ++vis[id[i]] == 1) ++siz; if(id[i - len] && !--vis[id[i - len]]) --siz; if(siz == fs) return 1; } return 0; } inline int bin() { int l = 0, r = n, mid; while(r > l + 1) { mid = (l + r) >> 1; check(mid) ? r = mid : l = mid; } return r; } int main() { static int m; static char s[1010]; scanf("%d", &m); for(int i = 1; i <= m; i++) scanf("%s", s + 1), tr.insert(s, strlen(s + 1), i); scanf("%d", &n); for(int i = 1; i <= n; i++) scanf("%s", s + 1), ++vis[id[i] = tr.find(s, strlen(s + 1))]; for(int i = 1; i <= m; i++) fs += (bool) vis[i]; if(!fs) return puts("0\n0"), 0; printf("%d\n%d\n", fs, bin()); return 0; }
4123:
类似「HEOI2016:排序」的线段树解法,枚举答案字符,按照相对大小转为0、1,将区间排序转为区间赋值、区间求和。
代码:
#include <iostream> #include <cstdio> #include <cstring> #define debug cerr using namespace std; const int maxn = 1e5 + 1e2; char in[maxn], ans[maxn]; int lst[maxn], v[maxn], o[maxn], l[maxn], r[maxn]; struct SegmentTree { int sum[maxn << 2], tag[maxn << 2]; #define lson(pos) (pos << 1) #define rson(pos) (pos << 1 | 1) inline void maintain(int pos) { sum[pos] = sum[lson(pos)] + sum[rson(pos)]; } inline void build(int pos, int l, int r) { tag[pos] = -1; if(l == r) return void(sum[pos] = v[l]); const int mid = (l + r) >> 1; build(lson(pos), l, mid), build(rson(pos), mid + 1, r), maintain(pos); } inline void apply(int pos, int l, int r, int v) { sum[pos] = (r - l + 1) * (tag[pos] = v); } inline void push(int pos, int l, int r, int mid) { if(!~tag[pos]) return; apply(lson(pos), l, mid, tag[pos]), apply(rson(pos), mid + 1, r, tag[pos]), tag[pos] = -1; } inline void update(int pos, int l, int r, int ll, int rr, int v) { if(ll > rr) return; if(ll <= l && r <= rr) return apply(pos, l, r, v); const int mid = (l + r) >> 1; push(pos, l, r, mid); if(ll <= mid) update(lson(pos), l, mid, ll, rr, v); if(mid < rr) update(rson(pos), mid + 1, r, ll, rr, v); maintain(pos); } inline int query(int pos, int l, int r, int ll, int rr) { if(ll <= l && r <= rr) return sum[pos]; const int mid = (l + r) >> 1; push(pos, l, r, mid); int ret = 0; if(ll <= mid) ret += query(lson(pos), l, mid, ll, rr); if(mid < rr) ret += query(rson(pos), mid + 1, r, ll, rr); return ret; } inline void dfs(int pos, int l, int r) { if(l == r) return void(v[l] = sum[pos]); const int mid = (l + r) >> 1; push(pos, l, r, mid); dfs(lson(pos), l, mid), dfs(rson(pos), mid + 1, r); } }sgt; int main() { static int n, m; scanf("%d%d%s", &n, &m, in + 1); for(int i = 1; i <= m; i++) scanf("%d%d%d", l + i, r + i, o + i); for(int al ='a'; al <= 'z'; al++) { memcpy(lst, v, sizeof lst); for(int i = 1; i <= n; i++) v[i] = in[i] <= al; sgt.build(1, 1, n); for(int i = 1; i <= m; i++) { int s1 = sgt.query(1, 1, n, l[i], r[i]), s0 = r[i] - l[i] + 1 - s1; if(!o[i]) sgt.update(1, 1, n, l[i], l[i] + s0 - 1, 0), sgt.update(1, 1, n, l[i] + s0, r[i], 1); else sgt.update(1, 1, n, l[i], l[i] + s1 - 1, 1), sgt.update(1, 1, n, l[i] + s1, r[i], 0); } sgt.dfs(1, 1, n); for(int i = 1; i <= n; i++) if(v[i] && !lst[i]) ans[i] = al; } puts(ans + 1); return 0; }
1402:
非旋Treap维护两个标记:赋值、加等差。
注意标记合并顺序为先赋值,后等差。应用赋值时候清除原有一切标记。
代码:
(size * size没转long long挂了一发,丢~人!)
#include <iostream> #include <cstdio> #define debug cerr typedef long long ll; using namespace std; struct Treap { struct Node { ll v, sum, fill_v, add_fir, add_d; bool tag_fill, tag_add; Node *ls, *rs; int siz, hv; Node(ll x): v(x), sum(x), siz(1), hv(rand()) { cleanTags(); } void cleanTags() { tag_fill = tag_add = fill_v = add_fir = add_d = 0; } void apply_fill(ll nv) { cleanTags(), tag_fill = 1, sum = siz * (v = fill_v = nv); } void apply_add(ll fir, ll d) { const int lsiz = ls ? ls->siz : 0; tag_add = 1, add_fir += fir, add_d += d; v += fir + d * lsiz, sum += (ll(siz) * (siz - 1) >> 1) * d + siz * fir; } void push() { if(tag_fill) { if(ls) ls->apply_fill(fill_v); if(rs) rs->apply_fill(fill_v); } if(tag_add) { const int lsiz = ls ? ls->siz : 0; if(ls) ls->apply_add(add_fir, add_d); if(rs) rs->apply_add(add_fir + add_d * (lsiz + 1), add_d); } cleanTags(); } void maintain() { sum = v, siz = 1; if(ls) sum += ls->sum, siz += ls->siz; if(rs) sum += rs->sum, siz += rs->siz; } }*root; pair<Node*, Node*> split(Node* pos, int tar) { if(!tar) return make_pair(nullptr, pos); const int lsiz = pos->ls ? pos->ls->siz : 0; pos->push(); if(tar <= lsiz) { auto ret = split(pos->ls, tar); pos->ls = ret.second, pos->maintain(); return make_pair(ret.first, pos); } else { auto ret = split(pos->rs, tar - lsiz - 1); pos->rs = ret.first, pos->maintain(); return make_pair(pos, ret.second); } } Node* merge(Node* l, Node* r) { if(l == nullptr || r == nullptr) return l == nullptr ? r : l; if(l->hv > r->hv) return l->push(), l->rs = merge(l->rs, r), l->maintain(), l; else return r->push(), r->ls = merge(l, r->ls), r->maintain(), r; } Treap() { root = nullptr; } ~Treap() { delete_All(root); } void delete_All(Node* pos) { if(pos == nullptr) return; if(pos->ls) delete_All(pos->ls); if(pos->rs) delete_All(pos->rs); delete pos; } void fill_v(int l, int r, ll v) { auto sp1 = split(root, l - 1), sp2 = split(sp1.second, r - l + 1); sp2.first->apply_fill(v); root = merge(sp1.first, merge(sp2.first, sp2.second)); } void add_v(int l, int r, ll fir, ll d) { auto sp1 = split(root, l - 1), sp2 = split(sp1.second, r - l + 1); sp2.first->apply_add(fir, d); root = merge(sp1.first, merge(sp2.first, sp2.second)); } void push(int pos, ll v) { Node* np = new Node(v); auto sp = split(root, pos - 1); root = merge(sp.first, merge(np, sp.second)); } ll query(int l, int r) { auto sp1 = split(root, l - 1), sp2 = split(sp1.second, r - l + 1); const ll ret = sp2.first->sum; root = merge(sp1.first, merge(sp2.first, sp2.second)); return ret; } }tp; int main() { static int n, q; scanf("%d%d", &n, &q); for(int i = 1, x; i <= n; i++) scanf("%d", &x), tp.push(i, x); for(int i = 1, o, l, r, x; i <= q; i++) { scanf("%d%d%d", &o, &l, &r); switch(o) { case 1: scanf("%d", &x), tp.fill_v(l, r, x); break; case 2: scanf("%d", &x), tp.add_v(l, r, x, x); break; case 3: tp.push(l, r); break; case 4: printf("%lld\n", tp.query(l, r)); } } return 0; }
好了,就酱。一堆水题还要发什么题解~还不是要填博客嘛qwq
我也是很难的啦~
对了,zoom课堂用这种头像是不是会被...啊
原图ID:Pixiv 80315592
(什么?没有梯子?这我可不能负责)
评论~ NOTHING