大一上第四周学习笔记

    科技2022-08-19  101

    10.5 周一

    国庆浪了好久

    其实浪完了我真的不知道要干啥了

    这种生活其实是很空虚的

    我以前以为算法竞赛占据了我太多时间,没有时间享受其他事情

    其实这说明我还不热爱它

    这是编程这件事使我的生活变得充实

    这也是我感兴趣,有天赋,有前景的东西

    为什么不全力以赴呢

    找回对编程的热爱,而不是为了保研

    想起了我以前看得《驱动力》这本书

    现实的利益这样外在驱动力其实是不长久,不稳定的。

    真正强大,稳定的驱动力是内在驱动力。

    对于我,就是热爱,享受解题的乐趣

    我不会花这么多时间在我不热爱的事情

    我编程,只因为我爱它

    大学找到自己热爱的事情并为之奋斗,是多快乐的事情

    接下来三天开启集训模式,自我集训。上午3小时,下午晚上一起3小时。

     

    洛谷 P1080 国王游戏

    #include<bits/stdc++.h> #define REP(i, a, b) for(int i = (a); i < (b); i++) #define _for(i, a, b) for(int i = (a); i <= (b); i++) using namespace std; const int MAXN = 1e4 + 10; struct node { int l, r; }a[MAXN]; bool cmp(node x, node y) { return x.r * x.l < y.r * y.l; } struct bignum { int s[MAXN], len; bignum() { memset(s, 0, sizeof(s)); len = 0; } //初始化 }; bignum ma(bignum a, bignum b) { if(a.len > b.len) return a; if(a.len < b.len) return b; for(int i = a.len; i >= 1; i--) { if(a.s[i] > b.s[i]) return a; if(a.s[i] < b.s[i]) return b; } return a; } bignum operator * (bignum a, int b) //注意这个先乘再进位再位数。不要从0开始弄位数,因为可能中间有0 { int& len = a.len; _for(i, 1, len) a.s[i] *= b; _for(i, 1, len) a.s[i+1] += a.s[i] / 10, a.s[i] %= 10; while(a.s[len+1]) { len++; //先加再处理进位 a.s[len+1] += a.s[len] / 10; a.s[len] %= 10; } return a; } bignum operator / (bignum a, int b) //这种重载运算符的写法非常的爽。同时复习高精度除以低精度,其实不难,自己写个竖式模拟一下就好了 { int yu = 0; bignum res; res.len = a.len; for(int i = a.len; i >= 1; i--) { int t = yu * 10 + a.s[i]; //把握好余数 res.s[i] = t / b; yu = t % b; } while(!res.s[res.len] && res.len > 0) res.len--; return res; } bignum init(int x) //int初始化成bignum { bignum res; int& len = res.len; //&主要是偷懒,不用写那么长 while(x) { len++; res.s[len] = x % 10; x /= 10; } return res; } void print(bignum x) //输出 { for(int i = x.len; i >= 1; i--) printf("%d", x.s[i]); } int main() { int n, x, y; scanf("%d%d%d", &n, &x, &y); REP(i, 0, n) scanf("%d%d", &a[i].l, &a[i].r); sort(a, a + n, cmp); bignum ans = init(0), sum = init(x); REP(i, 0, n) { ans = ma(ans, sum / a[i].r); sum = sum * a[i].l; } print(ans); return 0; }

    这题的价值太大了,真的非常锻炼我。独立做出,非常爽。这样的难度刚好

    这题有两个难点,一个是如何贪心,一个是如何高精度

    贪心方面可以拿两个大臣谁前谁后做个比较来得出

    高精度的话,这道题高精度搞懂,以后遇到题目里面有高精度就完全不怂了

    其实我大部分时间都在写高精度。以后遇到写个bignum就好

    诚实说绿题的难度比较适合我。也就是提高的难度比我水平高一点。多做绿题以及其之前的题

     

    10.6 周二

    每天训练结束都问自己有没有独立思考,有没有抄题解

    洛谷P2392 kkksc03考前临时抱佛脚

    #include<bits/stdc++.h> #define REP(i, a, b) for(int i = (a); i < (b); i++) #define _for(i, a, b) for(int i = (a); i <= (b); i++) using namespace std; int ans, s[10], a[30], sum, res; void dfs(int x, int s, int n) { if(s > sum / 2) return; res = max(res, s); if(x == n + 1) return; dfs(x + 1, s + a[x], n); dfs(x + 1, s, n); } int f(int n) { sum = res = 0; _for(i, 1, n) sum += a[i]; dfs(1, 0, n); return sum - res; } int main() { scanf("%d%d%d%d", &s[1], &s[2], &s[3], &s[4]); _for(i, 1, 4) { _for(j, 1, s[i]) scanf("%d", &a[j]); ans += f(s[i]); //printf("## %d\n", f(s[i])); } printf("%d\n", ans); return 0; } #include<bits/stdc++.h> #define REP(i, a, b) for(int i = (a); i < (b); i++) #define _for(i, a, b) for(int i = (a); i <= (b); i++) using namespace std; int ans, s[10], a[30], sum, res; int f(int n) { sum = res = 0; _for(i, 1, n) sum += a[i]; REP(k, 0, pow(2, n)) { int t = k, now = 0, p = 0; while(t) { p++; if(t & 1) now += a[p]; t >>= 1; } //printf("## now: %d\n", now); if(now <= sum / 2) res = max(res, now); } return sum - res; } int main() { scanf("%d%d%d%d", &s[1], &s[2], &s[3], &s[4]); _for(i, 1, 4) { _for(j, 1, s[i]) scanf("%d", &a[j]); ans += f(s[i]); } printf("%d\n", ans); return 0; }

    很容易想到最接近一半就行

    数组范围小,直接爆搜

    后来我试了一下用二进制枚举子集,也可以过,但时间复杂度要多乘一个n

    这道题还可以用01背包,不过太久我已经忘记,以后会复习动态规划的

     

    洛谷 P1443 马的遍历

    #include<bits/stdc++.h> #define REP(i, a, b) for(int i = (a); i < (b); i++) #define _for(i, a, b) for(int i = (a); i <= (b); i++) using namespace std; const int MAXN = 500; struct node{ int x, y; }; int Map[MAXN][MAXN], n, m, sx, sy; int dir[8][2] = {1, 2, 1, -2, -1, 2, -1, -2, 2, 1, 2, -1, -2, 1, -2, -1}; void bfs() { memset(Map, -1, sizeof(Map)); Map[sx][sy] = 0; queue<node> q; q.push(node{sx, sy}); while(!q.empty()) { node u = q.front(); q.pop(); REP(i, 0, 8) { int x = u.x + dir[i][0], y = u.y + dir[i][1]; if(Map[x][y] != -1 || x < 1 || x > n || y < 1 || y > m) continue; Map[x][y] = Map[u.x][u.y] + 1; q.push(node{x, y}); } } } int main() { scanf("%d%d%d%d", &n, &m, &sx, &sy); bfs(); _for(i, 1, n) { _for(j, 1, m) printf("%-5d", Map[i][j]); puts(""); } return 0; }

    复习bfs

     

    洛谷 P1135 奇怪的电梯

    #include<bits/stdc++.h> #define REP(i, a, b) for(int i = (a); i < (b); i++) #define _for(i, a, b) for(int i = (a); i <= (b); i++) using namespace std; const int MAXN = 250; int step[MAXN], k[MAXN], n, a, b, s[2] = {1, -1}; void bfs() { memset(step, -1, sizeof(step)); step[a] = 0; queue<int> q; q.push(a); while(!q.empty()) { int u = q.front(); q.pop(); REP(i, 0, 2) { int v = u + k[u] * s[i]; if(v < 1 || v > n || step[v] != -1) continue; step[v] = step[u] + 1; if(v == b) return; q.push(v); } } } int main() { scanf("%d%d%d", &n, &a, &b); _for(i, 1, n) scanf("%d", &k[i]); bfs(); printf("%d\n", step[b]); return 0; }

    bfs可以用来解决像这样a到b最短需要多少步的题目。这是bfs的优势所在,因为第一个搜到的一定是最短的

     

    复习的速度比我想的要快

    争取在寒假前把自己以前比较熟悉的部分全部掌握,加油

     

    复习一二维前缀和差分

    这个博客写的很好前缀和与差分

     

    一维前缀和

    支持O(1)查询[l,r]的和

    #include<bits/stdc++.h> #define REP(i, a, b) for(int i = (a); i < (b); i++) #define _for(i, a, b) for(int i = (a); i <= (b); i++) using namespace std; const int MAXN = 100; int a[MAXN], n, s[MAXN]; int main() { scanf("%d", &n); _for(i, 1, n) { scanf("%d", &a[i]); s[i] = s[i-1] + a[i]; } while(1) { int l, r; scanf("%d%d", &l, &r); printf("%d\n", s[r] - s[l-1]); } return 0; }

    一维差分

    支持O(1)使[l,r]上加上一个p,最后输出每一个元素

    #include<bits/stdc++.h> #define REP(i, a, b) for(int i = (a); i < (b); i++) #define _for(i, a, b) for(int i = (a); i <= (b); i++) using namespace std; const int MAXN = 100; int a[MAXN], n, d[MAXN]; int main() { scanf("%d", &n); _for(i, 1, n) { scanf("%d", &a[i]); d[i] = a[i] - a[i-1]; } while(1) { int l, r, p; scanf("%d%d%d", &l, &r, &p); if(l == 0 && r == 0) break; d[l] += p; d[r+1] -= p; } _for(i, 1, n) d[i] += d[i-1]; _for(i, 1, n) printf("%d ", d[i]); return 0; }

    二维前缀和

    #include<bits/stdc++.h> #define REP(i, a, b) for(int i = (a); i < (b); i++) #define _for(i, a, b) for(int i = (a); i <= (b); i++) using namespace std; const int MAXN = 100; int a[MAXN][MAXN], s[MAXN][MAXN], n, m; int main() { scanf("%d%d", &n, &m); _for(i, 1, n) _for(j, 1, m) { scanf("%d", &a[i][j]); s[i][j] = a[i][j] + s[i-1][j] + s[i][j-1] - s[i-1][j-1]; } while(1) { int x1, y1, x2, y2; scanf("%d%d%d%d", &x1, &y1, &x2, &y2); printf("%d\n", s[x2][y2] - s[x2][y1-1] - s[x1-1][y2] + s[x1-1][y1-1]); } return 0; }

    二维差分

    d[i][j] = a[i][j] - a[i-1][j] - a[i][j-1] + a[i-1][j-1]

    洛谷 P3397 地毯

    #include<bits/stdc++.h> #define REP(i, a, b) for(int i = (a); i < (b); i++) #define _for(i, a, b) for(int i = (a); i <= (b); i++) using namespace std; const int MAXN = 1e3 + 10; int d[MAXN][MAXN], n, m; int main() { scanf("%d%d", &n, &m); while(m--) { int x1, y1, x2, y2; scanf("%d%d%d%d", &x1, &y1, &x2, &y2); d[x1][y1]++; d[x1][y2+1]--; d[x2+1][y1]--; d[x2+1][y2+1]++; } _for(i, 1, n) { _for(j, 1, n) { d[i][j] += d[i-1][j] + d[i][j-1] - d[i-1][j-1]; printf("%d ", d[i][j]); } puts(""); } return 0; }

     

    洛谷 P1551 亲戚

    #include<bits/stdc++.h> #define REP(i, a, b) for(int i = (a); i < (b); i++) #define _for(i, a, b) for(int i = (a); i <= (b); i++) using namespace std; const int MAXN = 5000 + 10; int f[MAXN], n, m, p; int find(int x) { if(f[x] == x) return x; return f[x] = find(f[x]); } void Union(int x, int y) { f[find(x)] = find(y); } int main() { scanf("%d%d%d", &n, &m, &p); _for(i, 1, n) f[i] = i; while(m--) { int x, y; scanf("%d%d", &x, &y); Union(x, y); } while(p--) { int x, y; scanf("%d%d", &x, &y); if(find(x) == find(y)) puts("Yes"); else puts("No"); } return 0; }

    复习并查集。并查集代码好短,也很好理解,用来做集合的问题

     

    洛谷 P1102 A-B 数对

    #include<bits/stdc++.h> #define REP(i, a, b) for(int i = (a); i < (b); i++) #define _for(i, a, b) for(int i = (a); i <= (b); i++) using namespace std; const int MAXN = 2e5 + 10; int a[MAXN], n, c; map<int, int> s; int main() { scanf("%d%d", &n, &c); _for(i, 1, n) { scanf("%d", &a[i]); s[a[i]+c]++; } long long ans = 0; _for(i, 1, n) ans += s[a[i]]; printf("%lld\n", ans); return 0; }

    复习了map,一次操作复杂度logn

     

    大致规划

    整个10月,接下来复习树状数组线段树,图论,数学,杂项

    11月复习动态规划

    12月1月大量刷题,刷绿题

     

    10.7 周三

     

    洛谷 P3374 【模板】树状数组 1

    #include<bits/stdc++.h> #define REP(i, a, b) for(int i = (a); i < (b); i++) #define _for(i, a, b) for(int i = (a); i <= (b); i++) using namespace std; const int MAXN = 5e5 + 10; int c[MAXN], n, m; int lowbit(int x) { return x & (-x); } void add(int x, int p) { while(x <= n) { c[x] += p; x += lowbit(x); } } int s(int x) { int res = 0; while(x) { res += c[x]; x -= lowbit(x); } return res; } int sum(int x, int y) { return s(y) - s(x-1); } int main() { int k, x, y; scanf("%d%d", &n, &m); _for(i, 1, n) { scanf("%d", &x); add(i, x); } while(m--) { scanf("%d%d%d", &k, &x, &y); if(k == 1) add(x, y); else printf("%d\n", sum(x, y)); } return 0; }

    复习树状数组。单点修改,区间查询

     

    洛谷 P3368 【模板】树状数组 2

    #include<bits/stdc++.h> #define REP(i, a, b) for(int i = (a); i < (b); i++) #define _for(i, a, b) for(int i = (a); i <= (b); i++) using namespace std; const int MAXN = 5e5 + 10; int c[MAXN], n, m; int lowbit(int x) { return x & (-x); } void add(int x, int p) { while(x <= n) { c[x] += p; x += lowbit(x); } } int sum(int x) { int res = 0; while(x) { res += c[x]; x -= lowbit(x); } return res; } int main() { int k, x, y, t = 0; scanf("%d%d", &n, &m); _for(i, 1, n) { scanf("%d", &x); add(i, x - t); t = x; } while(m--) { scanf("%d", &k); if(k == 1) { scanf("%d%d%d", &x, &y, &t); add(x, t); add(y + 1, -t); } else { scanf("%d", &x); printf("%d\n", sum(x)); } } return 0; }

    区间修改,单点查询。用差分转化一下就好了

    二维树状数组也挺简单,也是支持单点修改区间查询,区间修改,单点查询

    #include<cstdio> #include<algorithm> #define REP(i, a, b) for(register int i = (a); i < (b); i++) #define _for(i, a, b) for(register int i = (a); i <= (b); i++) using namespace std; const int MAXN = 1e4; int f[MAXN][MAXN], n, m, p; inline int lowbit(int x) { return x & (-x); } inline void add(int x, int y, int q) { while(x <= n) { for(int i = y; i <= m; i += lowbit(i)) //注意for循环的写法 f[x][i] += q; x += lowbit(x); } } inline int sum(int x, int y) { int res = 0; while(x) { for(int i = y; i; i -= lowbit(i)) res += f[x][i]; x -= lowbit(x); } return res; } inline int ans(int x1, int y1, int x2, int y2) { return sum(x1, y1) + sum(x2-1, y2-1) - sum(x1, y2 - 1) - sum(x2 - 1, y1); } int main() { scanf("%d%d%d", &n, &m, &p); _for(i, 1, n) _for(j, 1, m) { int x; scanf("%d", &x); add(i, j, x); } while(p--) { int k, x, y, q, a, b; scanf("%d%d%d", &k, &x, &y); if(k == 1) scanf("%d", &q), add(x, y, q); else { scanf("%d%d", &a, &b); printf("%d\n", ans(a, b, x, y)); } } return 0; }

    洛谷 P3397 地毯

    #include<bits/stdc++.h> #define REP(i, a, b) for(int i = (a); i < (b); i++) #define _for(i, a, b) for(int i = (a); i <= (b); i++) using namespace std; const int MAXN = 1e3 + 10; int c[MAXN][MAXN], n, m, k; int lowbit(int x) { return x & (-x); } int add(int x, int y, int p) { while(x <= n) { for(int i = y; i <= m; i += lowbit(i)) c[x][i] += p; x += lowbit(x); } } int sum(int x, int y) { int res = 0; while(x) { for(int i = y; i; i -= lowbit(i)) res += c[x][i]; x -= lowbit(x); } return res; } void updata(int x1, int y1, int x2, int y2) { add(x1, y1, 1); add(x1, y2+1, -1); add(x2+1, y1, -1); add(x2+1, y2+1, 1); } int main() { scanf("%d%d", &n, &k); m = n; while(k--) { int x1, y1, x2, y2; scanf("%d%d%d%d", &x1, &y1, &x2, &y2); updata(x1, y1, x2, y2); } _for(i, 1, n) { _for(j, 1, n) printf("%d ", sum(i, j)); puts(""); } return 0; }

     

    洛谷 P1908 逆序对

    #include<bits/stdc++.h> #define REP(i, a, b) for(int i = (a); i < (b); i++) #define _for(i, a, b) for(int i = (a); i <= (b); i++) using namespace std; typedef long long ll; const int MAXN = 5e5 + 10; int c[MAXN], a[MAXN], b[MAXN], n; void read(int& x) { int sign = 1; x = 0; char ch = getchar(); while(!isdigit(ch)) { if(ch == '-') sign = -1; ch = getchar(); } while(isdigit(ch)) { x = x * 10 + ch - '0'; ch = getchar(); } x *= sign; } int lowbit(int x) { return x & -x; } void add(int x, int p) { while(x <= MAXN) { c[x] += p; x += lowbit(x); } } int sum(int x) { int res = 0; while(x) { res += c[x]; x -= lowbit(x); } return res; } int main() { scanf("%d", &n); REP(i, 0, n) read(b[i]), a[i] = b[i]; sort(b, b + n); int t = unique(b, b + n) - b; REP(i, 0, n) a[i] = lower_bound(b, b + t, a[i]) - b + 1; ll ans = 0; REP(i, 0, n) { ans += i - sum(a[i]); add(a[i], 1); } printf("%lld\n", ans); return 0; }

    独立做出,爽!

    学到了蛮多

    一 是复习了读入优化

    二 我做道题时很快就想到了树状数组的解法。但是1e9空间显然会炸。于是我用了map,因为map一次操作是logn,所以超时了50分

    后来我就想到了离散化,因为只有大小关系有价值,绝对大小没有价值

    离散化有两种方法,我用的是存在另外一个数组,排序去重,然后用原来的数组二分找出。

    还可以用结构体的方法,但是结构体的离散化不能去重,也就是说如果有重复的元素,绝对大小相同的会赋予不同的值。这种情况需要额外处理。

     

    洛谷 P3372 【模板】线段树 1

    #include<bits/stdc++.h> #define l(k) (k << 1) #define r(k) ((k << 1) + 1) #define REP(i, a, b) for(int i = (a); i < (b); i++) #define _for(i, a, b) for(int i = (a); i <= (b); i++) using namespace std; typedef long long ll; const int MAXN = 1e5 + 10; struct node { ll f, w; int l, r; int len() { return r - l + 1; } int m() { return (l + r) >> 1; } }t[MAXN << 2]; void lazy(int k, ll p) { t[k].f += p; t[k].w += t[k].len() * p; } void up(int k) { t[k].w = t[l(k)].w + t[r(k)].w; } void down(int k) { lazy(l(k), t[k].f); lazy(r(k), t[k].f); t[k].f = 0; } void build(int k, int l, int r) { t[k].l = l; t[k].r = r; t[k].f = 0; if(l == r) { scanf("%lld", &t[k].w); return; } int m = t[k].m(); build(l(k), l, m); build(r(k), m + 1, r); up(k); } void add(int k, int x, ll p) { if(t[k].len() == 1) { t[k].w += p; return; } if(t[k].f) down(k); int m = t[k].m(); if(x <= m) add(l(k), x, p); else add(r(k), x, p); up(k); } void change(int k, int L, int R, ll p) { if(L <= t[k].l && t[k].r <= R) { lazy(k, p); return; } if(t[k].f) down(k); int m = t[k].m(); if(L <= m) change(l(k), L, R, p); if(R > m) change(r(k), L, R, p); up(k); } ll sum(int k, int L, int R) { if(L <= t[k].l && t[k].r <= R) return t[k].w; if(t[k].f) down(k); ll res = 0; int m = t[k].m(); if(L <= m) res += sum(l(k), L, R); if(R > m) res += sum(r(k), L, R); return res; } int main() { int n, m, q, x, y, k; scanf("%d%d", &n, &m); build(1, 1, n); while(m--) { scanf("%d%d%d", &q, &x, &y); if(q == 1) { scanf("%d", &k); change(1, x, y, k); } else printf("%lld\n", sum(1, x, y)); } return 0; }

    线段树复习。这个代码量挺大的。今明两天每次做线段树题都从头开始打代码。

     

    10.9 周五

    加油,今天把线段树复习完

    每道难题至少想两三个小时,在想的过程中进步

     

    线段树每个数乘一个x的操作想半天想不出

     

    停一下,复习一下快速幂

    理解本质,其实就是b化为二进制而已

    #include<bits/stdc++.h> #define REP(i, a, b) for(int i = (a); i < (b); i++) #define _for(i, a, b) for(int i = (a); i <= (b); i++) using namespace std; int cal(int a, int b, int p) { int res = 1 % p; a %= p; while(b) { if(b & 1) res = res * a % p; b >>= 1; a = a * a % p; } return res; } int main() { int a, b, p; scanf("%d%d%d", &a, &b, &p); printf("%d\n", cal(a, b, p)); return 0; }

    复习Floyd模板。可以用来找最小权值环和判断连通性

    _for(i, 1, n) _for(j, 1, n) f[i][j] = (i == j ? 0 : 1e9) _for(k, 1, n) _for(i, 1, n) _for(j, 1, n) f[i][j] = min(f[i][j], f[i][k] + f[k][j]);

    复习堆优化dijstra。复杂度mlogn 只能用于没有负权边的情况

    #include<bits/stdc++.h> #define pb push_back #define REP(i, a, b) for(int i = (a); i < (b); i++) #define _for(i, a, b) for(int i = (a); i <= (b); i++) using namespace std; const int MAXN = 1e4 + 10; struct node { int v, w; bool operator < (const node& rhs) const { return w > rhs.w; } }; vector<node> g[MAXN]; int d[MAXN], n, m, s; void work() { _for(i, 1, n) d[i] = 1e9; d[s] = 0; priority_queue<node> q; q.push(node{s, 0}); while(!q.empty()) { node x = q.top(); q.pop(); int u = x.v; if(d[u] != x.w) continue; REP(i, 0, g[u].size()) { int v = g[u][i].v, w = g[u][i].w; if(d[v] > d[u] + w) { d[v] = d[u] + w; q.push(node{v, d[v]}); } } } } int main() { scanf("%d%d%d", &n, &m, &s); while(m--) { int u, v, w; scanf("%d%d%d", &u, &v, &w); g[u].pb(node{v, w}); } work(); _for(i, 1, n) { if(d[i] == 1e9) d[i] = (1 << 31) - 1; printf("%d ", d[i]); } return 0; }

    spfa,有负权边的情况

    #include<bits/stdc++.h> #define pb push_back #define REP(i, a, b) for(int i = (a); i < (b); i++) #define _for(i, a, b) for(int i = (a); i <= (b); i++) using namespace std; const int MAXN = 1e4 + 10; struct node { int v, w; }; vector<node> g[MAXN]; int d[MAXN], vis[MAXN], n, m, s; void work() { _for(i, 1, n) d[i] = 1e9; d[s] = 0; queue<int> q; q.push(s); vis[s] = 1; while(!q.empty()) { int u = q.front(); q.pop(); vis[u] = 0; REP(i, 0, g[u].size()) { int v = g[u][i].v, w = g[u][i].w; if(d[v] > d[u] + w) { d[v] = d[u] + w; if(!vis[v]) { q.push(v); vis[v] = 1; } } } } } int main() { scanf("%d%d%d", &n, &m, &s); while(m--) { int u, v, w; scanf("%d%d%d", &u, &v, &w); g[u].pb(node{v, w}); } work(); _for(i, 1, n) { if(d[i] == 1e9) d[i] = (1 << 31) - 1; printf("%d ", d[i]); } return 0; }

     

    洛谷 P1629 邮递员送信

    卡了好久

    其实很快就想到了建反图

    但是想不清楚觉得没用

    后来我在纸上随便画了一下发现画反图就是答案

    所以一定一定要在草稿纸上画图,把脑子里的想法形象化,清晰化

    #include<bits/stdc++.h> #define REP(i, a, b) for(int i = (a); i < (b); i++) #define _for(i, a, b) for(int i = (a); i <= (b); i++) using namespace std; const int MAXN = 1e3 + 10; struct node { int v, w; bool operator < (const node& rhs) const { return w > rhs.w; } }; vector<node> g[MAXN], k[MAXN]; int d[MAXN], n, m; void work1() { _for(i, 1, n) d[i] = 1e9; d[1] = 0; priority_queue<node> q; q.push({1, 0}); while(!q.empty()) { node x = q.top(); q.pop(); int u = x.v; if(d[u] != x.w) continue; REP(i, 0, g[u].size()) { int v = g[u][i].v, w = g[u][i].w; if(d[v] > d[u] + w) { d[v] = d[u] + w; q.push(node{v, d[v]}); } } } } void work2() { _for(i, 1, n) d[i] = 1e9; d[1] = 0; priority_queue<node> q; q.push({1, 0}); while(!q.empty()) { node x = q.top(); q.pop(); int u = x.v; if(d[u] != x.w) continue; REP(i, 0, k[u].size()) { int v = k[u][i].v, w = k[u][i].w; if(d[v] > d[u] + w) { d[v] = d[u] + w; q.push(node{v, d[v]}); } } } } int main() { scanf("%d%d", &n, &m); while(m--) { int u, v, w; scanf("%d%d%d", &u, &v, &w); g[u].push_back(node{v, w}); k[v].push_back(node{u, w}); } int ans = 0; work1(); _for(i, 2, n) ans += d[i]; work2(); _for(i, 2, n) ans += d[i]; printf("%d\n", ans); return 0; }

    10.10 周六

    今天怒干支持每一个数支持乘法的线段树,灵光一闪有了突破的进展,但交上去只有30分,但比0分好多了

    然后实在不知错哪里,就放一放

    复习了一下最小生成树

    生成树就是用n-1条边联通

    Kruskal其实很简单

    贪心,边权从小到大连,能连就连

    #include<bits/stdc++.h> #define REP(i, a, b) for(int i = (a); i < (b); i++) #define _for(i, a, b) for(int i = (a); i <= b; i++) using namespace std; const int MAXN = 5e3 + 10; struct node { int u, v, w; bool operator < (const node& rhs) const { return w < rhs.w; } }; vector<node> Edge; int f[MAXN], n, m; int find(int x) { if(f[x] == x) return x; return f[x] = find(f[x]); } int main() { scanf("%d%d", &n, &m); while(m--) { int u, v, w; scanf("%d%d%d", &u, &v, &w); Edge.push_back(node{u, v, w}); } _for(i, 1, n) f[i] = i; sort(Edge.begin(), Edge.end()); int ans = 0, cnt = 0; REP(i, 0, Edge.size()) { int u = find(Edge[i].u), v = find(Edge[i].v); if(u != v) { f[u] = v; ans += Edge[i].w; cnt++; } } if(cnt != n - 1) puts("orz"); else printf("%d\n", ans); return 0; }

     

    明天刚线段树那道题以及最小生成树的一些题目

    加油

     

    10.11 周日

     

    洛谷 p1194 买礼物

    为何要用最小生成树呢

    贪心加边,加完为止,这个过程就是最小生成树

    我写完交上去90分

    后来想到会不会kij还大于a,然后建边处理了一下,就ac了

    其实这组数据加的毫无必要,因为这组数据不符合题意,题目说了很清楚是优惠,就不可能存在这种情况

    #include<bits/stdc++.h> #define REP(i, a, b) for(int i = (a); i < (b); i++) #define _for(i, a, b) for(int i = (a); i <= (b); i++) using namespace std; const int MAXN = 1e3 + 10; struct node { int u, v, w; bool operator < (const node& rhs) const { return w < rhs.w; } }; vector<node> Edge; int f[MAXN], a, n, cnt, ans; int find(int x) { if(f[x] == x) return x; return f[x] = find(f[x]); } int main() { scanf("%d%d", &a, &n); _for(i, 1, n) f[i] = i; _for(i, 1, n) _for(j, 1, n) { int w; scanf("%d", &w); if(w != 0 && j > i && w <= a) Edge.push_back(node{i, j, w}); } cnt = n; sort(Edge.begin(), Edge.end()); REP(i, 0, Edge.size()) { int u = find(Edge[i].u), v = find(Edge[i].v); if(u != v) { f[u] = v; ans += Edge[i].w; cnt--; } } ans += cnt * a; printf("%d\n", ans); return 0; }

    p1991 无线通讯网

    二分+最小生成树

    审题时看到最大值最小或最小值最大要想到二分答案

    注意逆向思考,有时反过来想就对了

    #include<bits/stdc++.h> #define REP(i, a, b) for(int i = (a); i < (b); i++) #define _for(i, a, b) for(int i = (a); i <= (b); i++) using namespace std; const int MAXN = 1e3 + 10; struct node { int u, v; double w; bool operator < (const node& rhs) const { return w < rhs.w; } }; vector<node> Edge, ans; int f[MAXN], vis[MAXN], k, n; double x[MAXN], y[MAXN]; int find(int x) { if(f[x] == x) return x; return f[x] = find(f[x]); } double dist(int a, int b) { return sqrt(pow(x[a]-x[b], 2) + pow(y[a]-y[b], 2)); } bool check(double key) { _for(i, 1, n) f[i] = i; memset(vis, 0, sizeof(vis)); int cnt = 0; REP(i, 0, Edge.size()) { int u = find(Edge[i].u), v = find(Edge[i].v); if(u != v) { f[u] = v; if(Edge[i].w > key) { cnt += !vis[u] + !vis[v]; vis[u] = vis[v] = 1; } } } return cnt <= k; } int main() { scanf("%d%d", &k, &n); _for(i, 1, n) scanf("%lf%lf", &x[i], &y[i]); _for(i, 1, n) _for(j, i + 1, n) Edge.push_back(node{i, j, dist(i, j)}); sort(Edge.begin(), Edge.end()); double l = 0, r = 2e4; while(l + 1e-4 < r) { double mid = (l + r) / 2; if(!check(mid)) l = mid; else r = mid; } printf("%.2lf\n", r); return 0; }

     

    Processed: 0.019, SQL: 9