7月、8月考试总结(1)
7.10: Test1 惨遭爆零 TAT
第一题 在本地测是过了的 到了教师机莫名输出0 ?? 一道spfa的板子题 还做过 WA了也就GG。。[POJ2502]Subway
后来发现了 结构体强转有大问题???? woc
第二题 听大佬讲 有三种方法
1是floyd判一下 f[x][y] ?= f[x][k] + f[k][y] 还标记个flag(是否存在);
2是一个spfa找负环 这需要建一个神奇的图;
3是一个诡异的贪心 (左端相等 减去左边相等的数 再加入优先队列 如果区间相等 判断数是否相等 不相等就false)。
反正我都没想到 (难道高斯消元+区间离散 不能做?? 到时候再研究)洛谷【P2294】[HNOI2005]狡猾的商人
后来点开标签看了看 原来是差分约束(虽然原来好像讲过,但根本不记得了) 去网上找了篇好博文 终于懂了
第三题 一道强联通分量缩点 + 树形(DAG)dp 没时间写了 也只好GG 洛谷【P3436】[POI2006]PRO-Professor Szu
总结:先做思路更明显的题 程序注意细节 不要瞎搞 再接再厉 明天继续 fighting
7.12:Test2 130分。。(一般人拿的分qwq)
第一题 一道求解涂色方案数问题 判是否可行 看到就一脸懵 以为又是一道数学找规律题 草草地打完了爆搜就没去动了 后来回头想来写dp没写出来QAQ
正解是矩阵快速幂(膜一波hy大大 构造一个可行型矩阵,乘自己n-1次(用快速幂做),最后再乘上dp矩阵) 还有种神奇方法 倍增思想来做dp 两种时间复杂度差不多都是O(log n)
第二题 一道trie的裸题,但是数据卡trie,我就竟然奇迹般地写出了hash(似乎还是正解) 竟然时间效率还不错。以后要多多练练hash。 [HDU1075]What are you talking about?
第三题 一道拓欧的题,不知道拓欧咋写,又咋去用。。。 反正不太会QAQ。。 后来听了几位大牛的讲解 终于搞懂了qwq。。 [POJ2155]C Looooops
总结:有时候脑洞还是要开大一点 有可能灵机一闪的思路是正解
7.13:Test3 200分 (数据太水了。。。正解基本没过。。暴力还有a+b都能AC TAT)
第一题 一道平面几何题 计算任意两点之间距离的最大值 暴力全部秒过= = 正解是先做一遍凸包 把凸包上的点保留 再做一遍暴枚[POJ2187]Beauty Contest
第二题 一道求最大团的题(一开始不知道 写了个点枚暴力复杂度O(n^2 * 2^n)特别高QAQ)后来才知道是一道NP难问题(NPC)GG第一次碰 可以用Bron–Kerbosch算法(但好像会TLE)其实根据这道题的特殊性
可以把原图延伸为一个补图(将所有原图原本存在的边删掉加上不存在的边)然后跑一边二分图最大匹配 这样就可以把原图中互相不认识的一些点删掉 最后答案就是nl+nr-res [POJ3692]Kindergarten
第三题 一道搜索剪枝的题 (考试时候剪枝剪多了WA了 TAT) 有一个玄学剪枝 就是(maxv - sumv) / r * 2 + sums >= ans 这个就将体积换成侧面积来计算接下来的最大面积 这个似乎优化很多
一开始改了很久 发现枚举高度时 不能直接从h-1开始 有时要从(maxv-sumv-minv[deep-1]) / (i*i)(可用体积除以半径)开始 就是两个的最小值 [POJ1190]生日蛋糕
7.14:Test4 170分 (这次比较正常。。发挥刚好。)
第一题 一道最长不上升和不下降的裸题 不能用O(n^2)的算法来做 (n=100000) 要用 二分当前最优解的方法去写。 有点类似于合唱队型。。洛谷【P1091】合唱队形
第二题 一道特殊的01背包 考试没看出来(因为是乘积)最后只好写了一个DFS 去搜索 结果挂了 只有20分TAT 其实就是按减少饥饿值从大到小排序 如果当前减少饥饿值为负数 就不算 否则就更新f[i](i表示减少饥饿值为i时 最大的战斗力) 最后线性扫一遍就行了 [POJ2184]Cow Exhibition
第三题 又是一道报复社会的数论题。。 就是筛素数 要用上一种特殊的筛法 Miller-Rabin 这个就需要一些数论基础了 而且为了防爆范围 需要取模乘(类似于快速幂) 而且还需要一个快速幂来计算 这个运用了费马小定理 还有 二次探测定理 [POJ1811]Prime Test
7.18:Test5 50分 (TAT倒数第四(还有小朋友) 要被丢出去了)
第一题 一道搜索贪心 或者 递推dp 太自信了 5min打完就做后面了 没认真看看TAT
第二题 一道神奇的贪心 将根节点能选的最多节点数计算出来(sum) 再计算出从大到小sum个数的和再加起来。。。没想出来TAT
第三题 这道题就也是报复社会了。。。暴力打挂了,flag前面特判错了QAQ。。。正解用线段树进行维护 十分复杂(可以媲美工业题了)
总结:真的要注重细节啊!!!!! 明天NOI同步赛了。。 争取拿分 沉下心来 好好弄!!!
8.2:Test6 120分 (成功诠释暴力骗分的正确方法2333) PS:以后开始附代码了(还有他们的解题报告。。)
第一题 一道神奇的图论题(我考试时并没有理解题目的真正含义,靠暴力三重循环竟然骗了40分0.0)正解是类似于DAG dp,就是计算每一个点能到达的节点个数,因为能到达的节点必须要扩充边。需要用bitset来维护这个集合,可以进行数位优化,除以一个ω。为了防止爆空间,所以跑两遍。。(dy的程序是真的强,十分精炼……)
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 #include <cmath> 6 #include <bitset> 7 using namespace std; 8 #define For(i, l, r) for(int i = (l), _end_ = (int)(r); i <= _end_; ++i) 9 #define Fordown(i, r, l) for(int i =(r),_end_ = (int)(l); i >= _end_; --i) 10 #define Set(a, v) memset(a, v, sizeof(a)) 11 12 inline int read() { 13 int x = 0, fh = 1; char ch; 14 for(; !isdigit(ch); ch = getchar()) if(ch == '-') fh = -1; 15 for(; isdigit(ch); ch = getchar()) x =(x<<1) + (x<<3) + (ch ^ '0'); 16 return x * fh; 17 } 18 19 const int N = 60100, M = 100100; 20 int to[M], Next[M], Head[N], e = 0; 21 void add_edge (int u, int v) { 22 to[++e] = v; 23 Next[e] = Head[u]; 24 Head[u] = e; 25 } 26 27 bitset<N/2> G[N]; 28 bool vis[N]; 29 int n, m, ans = 0; 30 void dfs (int u, bool flag) { 31 if (vis[u]) return; 32 vis[u] = true; 33 if (flag && u <= (n>>1) ) G[u][u] = true; 34 else if (!flag && u > (n>>1) ) G[u][u - (n>>1)] = true; 35 for (int i = Head[u]; i; i = Next[i]) { 36 int v = to[i]; 37 dfs (v, flag); 38 G[u] |= G[v]; 39 } 40 ans += G[u].count(); 41 } 42 43 int main(){ 44 freopen ("worldline.in", "r", stdin); 45 freopen ("worldline.out", "w", stdout); 46 n = read(); m = read(); 47 For (i, 1, m) { 48 int u = read(), v = read(); 49 add_edge (u, v); 50 } 51 For (i, 1, n) dfs (i, true); 52 Set(vis, false); 53 For (i, 1, n) G[i].reset(); 54 For (i, 1, n) dfs (i, false); 55 printf ("%d\n", ans - n - m); 56 return 0; 57 }
第二题 一道看起来是道网络流,其实是一道贪心的题目0.0(题目把我们带走远了,dy出题真的强……)就是将左节点排序,然后去从左至右,一个个贪心去比较,维护序列用map或者set……(不得不说dy的STL也用的出神入化,不愧是进队爷……QAQ)
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <cmath> using namespace std; #define For(i, l, r) for(int i = (l), _end_ = (int)(r); i <= _end_; ++i) #define Fordown(i, r, l) for(int i =(r),_end_ = (int)(l); i >= _end_; --i) #define Set(a, v) memset(a, v, sizeof(a)) inline int read() { int x = 0, fh = 1; char ch; for(; !isdigit(ch); ch = getchar()) if(ch == '-') fh = -1; for(; isdigit(ch); ch = getchar()) x =(x<<1) + (x<<3) + (ch ^ '0'); return x * fh; } const int N = 100100; struct node { int l, r, s; bool operator < (const node &rhs) const { return l < rhs.l; } }; node a[N], b[N]; int n, m; typedef long long ll; #include <map> map<int, ll> M; void solve() { int j = 1; For (i, 1, n) { for(;j <= m && b[j].l <= a[i].l; ++j) if (!M.count(b[j].r) ) M[b[j].r] = b[j].s; else M[b[j].r] += b[j].s; while (a[i].s) { map<int, ll>::iterator p = M.lower_bound(a[i].r); if (p == M.end() ) {puts("No"); return;} if (a[i].s < p -> second) {p -> second -= a[i].s; a[i].s = 0;} else {a[i].s -= p -> second; p -> second = 0; M.erase(p);} } } puts("Yes"); return; } int main(){ int t = read(); while (t--) { M.clear(); n = read(); m = read(); For (i, 1, n) a[i] = (node) {read(), read(), read()}; For (i, 1, m) b[i] = (node) {read(), read(), read()}; sort (a + 1, a + 1 + n); sort (b + 1, b + 1 + m); solve(); } return 0; }
第三题 一道题面看了很久看懂,只会打杨辉三角暴力的,恐怖数论题……正解根本看不懂啊QAQ 数论还是太弱了……
8.3:Test7 110分(再次成功诠释暴力出奇迹的神奇方法2333)
第一题 一道概率题 原来从来都不会概率……被逼无奈 写了O(2^n)的暴力来推算……60分的概率dp O(n^2) 也有点难度,也不会QAQ……正解是O(n)的递推……要将递增的每一项对最后期望值的贡献求出来,最后还要将k对它的影响给表示出来……反正很变态= =
#include<bits/stdc++.h> using namespace std; const int maxn = 1000000 + 10; int n, k; double P[maxn]; double p, x, x2, y, ans; int main() { scanf("%d%d", &n, &k); for(int i = 0; i < n; i++) { scanf("%lf", &p); P[i] = p; ans += p * (3 * x2 + 3 * x + 1); x2 = p * (x2 + 2 * x + 1); x = p * (x + 1); ans -= (1.0-p) * (2 * y + 1); y = (1.0-p) * (y + 1); } ans += k * P[0] * P[n-1]; ans -= k * (1-P[0]) * (1-P[n-1]); printf("%lf\n", ans); return 0; }
第二题 一道看起来就不会做的题(事实上还是不会做),乘法逆元加上一个区间的变换……反正都不知道……TAT 只挂个正解的代码在上面吧
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int maxn = 500 + 5; const int mod = 998244353; int n, m; int a[maxn], d[maxn]; LL S[maxn][maxn]; LL dp[2][maxn][maxn], chs[maxn]; #define ADD(a, b) ((a += b) >= mod ? a -= mod : a) #define NONE(i, j) (chs[i-1] + chs[n-j] + chs[j-i+1]) LL fpm(LL base, LL exp) { LL ans = 1; for(; exp > 0; exp >>= 1) { if(exp & 1) ans = ans * base % mod; base = base * base % mod; } return ans; } void calc(int l, int r, int pos) { bool cur = 1; for(int i = l; i <= r; ++i) for(int j = i; j <= r; ++j) dp[0][i][j] = 0; dp[0][l][r] = 1; static LL sum = 0; for(int t = 0; t < m; ++t) { LL (*now)[maxn] = dp[cur], (*lst)[maxn] = dp[cur^1]; for(int i = l; i <= r; ++i) for(int j = i; j <= r; ++j) now[i][j] = lst[i][j] * NONE(i, j); for(int i = l; i <= r; ++i) { sum = 0; for(int j = r; j >= i; --j) { now[i][j] = (now[i][j] + sum); sum = (sum + lst[i][j] * (n-j)); } } for(int j = r; j >= l; --j) { sum = 0; for(int i = l; i <= j; ++i) { now[i][j] = (now[i][j] + sum) % mod; sum = (sum + lst[i][j] * (i-1)); } }cur ^= 1; } for(int i = l; i <= r; ++i) for(int j = i; j <= r; ++j) { ADD(S[i][pos], dp[cur^1][i][j]); ADD(S[j+1][pos], mod-dp[cur^1][i][j]); } } int main() { freopen("B.in", "r", stdin); freopen("B.out", "w", stdout); scanf("%d%d", &n, &m); for(int i = 1; i <= n; ++i) chs[i] = i * (i+1) >> 1; for(int i = 1; i <= n; ++i) scanf("%d", a + i), d[i] = a[i]; sort(d + 1, d + n + 1); for(int i = 1; i <= n; ++i) { int l = i, r = i; while(l > 1 && a[l-1] <= a[i]) --l; while(r < n && a[r+1] <= a[i]) ++r; calc(l, r, lower_bound(d + 1, d + n + 1, a[i]) - d); } for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) { ADD(S[i][j], S[i-1][j]); } LL INV = fpm(n * (n + 1) >> 1, LL (mod - 2) * m); for(int i = 1; i <= n; ++i) { LL ans = 0, tmp = 0; for(int j = 1; j <= n; ++j) if(S[i][j]) { ans = (ans + (S[i][j] - tmp) * d[j]) % mod; tmp = S[i][j]; } ans = ans * INV % mod; printf("%lld\n", (ans + mod) % mod); } return 0; }
第三题 一道我用暴力骗了90分的题目(Wearry良心出题人QwQ)
正解是先求出dfs序,再用背包在上面跑,标程有些细节还是没看懂……引用wt大佬的博客算了……
#include<bits/stdc++.h> using namespace std; void chkmax(int & a, int b) { if(a < b) a = b; } const int oo = 0x3f3f3f3f; const int maxn = 5000 + 10; const int maxm = 10000 + 10; int st[maxn], nxt[maxm], to[maxm], e = 1; void addedge(int u, int v) { to[++e] = v; nxt[e] = st[u]; st[u] = e; to[++e] = u; nxt[e] = st[v]; st[v] = e; } int dp[maxn][maxn]; //这个dp的第一维下标是更新后的dfs序 int n, m, dfs_clock; int dfn[maxn], efn[maxn]; int id[maxn], p[maxn], d[maxn]; void dfs(int u, int f = 0) { id[dfn[u] = ++ dfs_clock] = u; for(int i = st[u]; i; i = nxt[i]) if(to[i] != f) dfs(to[i], u); efn[u] = dfs_clock + 1; //记录u节点能到的最远的儿子编号+1 即选这个节点的祖先节点 能选到的下一个点 } int main() { freopen("C.in", "r", stdin); freopen("C.out", "w", stdout); scanf("%d%d", &n, &m); for(int i = 1; i <= n; ++i) scanf("%d%d", d + i, p + i); for(int i = 1; i < n; ++i) { static int u, v; scanf("%d%d", &u, &v); addedge(u, v); } dfs(1); memset(dp, ~oo, sizeof dp); dp[1][0] = 0; //根节点不取时默认为0 int ans = 0; for(int i = 1; i <= n; ++i) { int u = id[i]; for(int j = 0; j <= m; ++j) if(dp[i][j] >= -oo) { //负无穷更新其他的毫无意义…… chkmax(ans, dp[i][j]); //更新当前答案的最大值 chkmax(dp[efn[u]][j], dp[i][j]); //更新 if(j + p[u] <= m) //如果还在容量之内 chkmax(dp[i+1][j + p[u]], dp[i][j] + d[u]); //更新下一个(儿子或者efn)的背包 } } for(int i = 0; i <= m; ++i) chkmax(ans, dp[n+1][i]); printf("%d\n", ans); return 0; }
8.3:Test8 5分(这个分数太恐怖了……题目有些BT……但我的暴力也没打好,细节没注意QAQ)
第一题 一道二分答案加堆维护的题目……细节比较多……需要有些神奇思路,以及一些方法处理
第二题 一道贼难的数论题……好不容易推出了暴力,结果还打错了TAT 正解运用的定理,我一个都不懂,以后要恶补数论了啊!!
第三题 一道坑比的生成树题目……TAT 第一个点开始就要long long了 结果最大值开小了 GG了 正解也不懂 一个01 trie有些变态…… 20%的快速幂也没混到,贪了,数组小了TAT
30分解法见大佬博客2333 hzwer