\(T1\ 100\ Pts\)
\(T2\ 100\ Pts\)
\(T3\ 55\ Pts\)
总分: \(255\ Pts\)
五分钟过 \(T1\)
二十分钟过 \(T2\)
一个小时写完 \(T3\) 部分分
弃 再之后没有什么进展
感觉像是贪心专场
众所周知 贪心不需要证明
贪心
前面比后面的大 答案累加两者的差
/* Time: 6.5 Worker: Blank_space Source: */ /*--------------------------------------------*/ #include<cstdio> #include<cstring> #define int long long /*--------------------------------------头文件*/ const int A = 1e4 + 7; const int B = 1e5 + 7; const int C = 1e6 + 7; const int D = 1e7 + 7; const int mod = 1e9 + 7; const int INF = 0x3f3f3f3f; /*------------------------------------常量定义*/ inline void File() { freopen("road.in", "r", stdin); freopen("road.out", "w", stdout); } /*----------------------------------------文件*/ int n, d[B], ans; /*------------------------------------变量定义*/ inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();} while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();} return x * f; } /*----------------------------------------快读*/ /*----------------------------------------函数*/ signed main() { File(); n = read(); d[n + 1] = 0; for(int i = 1; i <= n; i++) d[i] = read(); for(int i = 1; i <= n; i++) if(d[i] > d[i + 1]) ans += d[i] - d[i + 1]; printf("%lld", ans); return 0; } /* 贪心 五分钟 过大样例 */
完全背包裸题
注意一下容量取最大值就能过
/* Time: 6.5 Worker: Blank_space Source: */ /*--------------------------------------------*/ #include<cstdio> #include<cstring> #include<algorithm> #define Max(x, y) ((x) > (y) ? (x) : (y)) #define emm(x) memset(x, 0, sizeof x) /*--------------------------------------头文件*/ const int B = 1e5 + 7; const int mod = 1e9 + 7; const int INF = 0x3f3f3f3f; /*------------------------------------常量定义*/ inline void File() { freopen("money.in", "r", stdin); freopen("money.out", "w", stdout); } /*----------------------------------------文件*/ int T, n, a[110], m, ans; bool f[B], vis[110]; /*------------------------------------变量定义*/ inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();} while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();} return x * f; } /*----------------------------------------快读*/ void work() { n = read(); ans = n; emm(f); emm(vis); f[0] = 1; m = 0; for(int i = 1; i <= n; i++) a[i] = read(), m = Max(m, a[i]), vis[i] = 1; std::sort(a + 1, a + 1 + n); for(int i = 1; i <= n; i++) { for(int j = a[i]; j <= m; j++) f[j] |= f[j - a[i]]; for(int j = i + 1; j <= n; j++) if(vis[j] && f[a[j]]) vis[j] = 0, ans--; } printf("%d\n", ans); } /*----------------------------------------函数*/ int main() { File(); T = read(); while(T--) work(); return 0; } /* 2 4 3 19 10 6 5 11 29 13 19 17 有点类似背包 完全背包 n 比较小 缀和求容量 贪一下 小的放在前面 跑个背包 小样例过了 大样例过了 但是跑的有点慢 跑了五秒多 只需要考虑能否凑出 容量从前缀和改为最大值 过了 20分钟 过大样例 */
\(55\) 的部分分是送的 略过了
代码
/* Time: 6.5 Worker: Blank_space Source: */ /*--------------------------------------------*/ #include<cstdio> #include<cstring> #include<algorithm> #define int long long #define Max(x, y) ((x) > (y) ? (x) : (y)) #define Min(x, y) ((x) < (y) ? (x) : (y)) #define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y)) /*--------------------------------------头文件*/ const int A = 5e4 + 7; const int mod = 1e9 + 7; const int INF = 0x3f3f3f3f; /*------------------------------------常量定义*/ inline void File() { freopen("track.in", "r", stdin); freopen("track.out", "w", stdout); } /*----------------------------------------文件*/ int n, m, du[A], f[A], g[A], ans, maxdu, sum, cnt, _d, dp[A]; struct node {int u, v, w;} a[A]; struct edge {int v, w, nxt;} e[A << 1]; int head[A], ecnt; std::pair <int, int> d[A]; /*------------------------------------变量定义*/ inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();} while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();} return x * f; } /*----------------------------------------快读*/ void add_edge(int u, int v, int w) {e[++ecnt] = (edge){v, w, head[u]}; head[u] = ecnt;} void dfs1(int u, int pre) { for(int i = head[u]; i; i = e[i].nxt) { int v = e[i].v, w = e[i].w; if(v == pre) continue; dfs1(v, u); if(f[v] + w > f[u]) g[u] = f[u], f[u] = f[v] + w; else if(f[v] + w > g[u]) g[u] = f[v] + w; } ans = Max(ans, f[u] + g[u]); } void work1() { dfs1(1, 0); printf("%lld", ans); } void dfs2(int u, int pre, int _s, int x) { if(_s >= x) cnt++, _s = 0; for(int i = head[u]; i; i = e[i].nxt) { int v = e[i].v, w = e[i].w; if(v == pre) continue; dfs2(v, u, _s + w, x); } } bool check2(int x) { cnt = 0; dfs2(1, 0, 0, x); return cnt >= m; } void work2() { int l = 0, r = sum; while(l <= r) { int mid = l + r >> 1; if(check2(mid)) l = mid + 1, ans = mid; else r = mid - 1; } printf("%lld", ans); } bool cmp(node x, node y) {return x.w > y.w;} bool check3(int x) { cnt = 0; for(int i = 1, j = n - 1; i <= j; i++) { if(a[i].w >= x) cnt++; else { if(i == j) break; while(a[i].w + a[j].w < x && i < j - 1) j--; if(a[i].w + a[j].w >= x) cnt++, j--; else break; } } return cnt >= m; } void work3() { std::sort(a + 1, a + n, cmp); int l = 0, r = sum; while(l <= r) { int mid = l + r >> 1; if(check3(mid)) l = mid + 1, ans = mid; else r = mid - 1; } printf("%lld", ans); } /*----------------------------------------函数*/ signed main() { File(); n = read(); m = read(); for(int i = 1; i < n; i++) { int x = read(), y = read(), z = read(); a[i] = (node){x, y, z}; sum += z; du[x]++; du[y]++; maxdu = Max(maxdu, Max(du[x], du[y])); add_edge(x, y, z); add_edge(y, x, z); } if(m == 1) work1(); else if(maxdu == 2) work2(); else if(maxdu == n - 1) work3(); else puts("18109"); return 0; } /* 最小最大 二分答案 先扔了 看数据 m = 1 树的直径 20 7 1 1 2 10 1 3 5 2 4 9 2 5 8 3 6 6 3 7 7 链 20 每条赛道一定是连续的一段 二分答案 进行check 二分一个最小值 8 x 1 2 7 3 4 2 2 3 3 7 8 1 6 7 4 5 6 5 4 5 6 过小样例 应该差不多 菊花图 15 一条赛道最多只能有两条边 二分答案 进行check 按边权排序 双指针 贪 10 x 1 3 6 1 10 1 1 2 8 1 4 9 1 9 7 1 5 3 1 6 4 1 7 2 1 8 8 过小样例 跑路 大概能过前 11 个点 二叉树 25 12 - 16 没思路 感觉这个已经接近正解了 尝试开正解 一棵树 最多只能有一条边连出去 贪一下 能在子树内连的尽量在子树内连 尝试维护唯一一条连出去的边 一定是除却已经连接的边的最长的边 父节点中 用所有儿子中连出的这条边进行相互匹配 排序在匹配复杂度爆炸 写不出来 弃了 卒 */
正解
看题很自然的想到二分答案 而且发现我上面那个贪心思路是对的... 想到了排序内部匹配 没想到二分查找 假掉了
二分一个最小值 进行check
直接递归到最底层 回溯的时候在每一棵子树内进行自匹配 对于一条没有被选过的边 通过排序 + 二分查找 找一条最短的能够满足条件的边进行匹配 并把这条边标记 对于每一个节点 维护从这棵子树内出去(还没有被匹配)的最长的链 再回过头来看上面的自匹配 把当前节点的以儿子节点为根的子树内出来的最长链加上那一条边 再存一个数组 排序后在这个数组中进行操作
代码
/* Time: 6.5 Worker: Blank_space Source: */ /*--------------------------------------------*/ #include<cstdio> #include<cstring> #include<algorithm> #define int long long #define Max(x, y) ((x) > (y) ? (x) : (y)) #define Min(x, y) ((x) < (y) ? (x) : (y)) #define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y)) /*--------------------------------------头文件*/ const int A = 5e4 + 7; const int mod = 1e9 + 7; const int INF = 0x3f3f3f3f; /*------------------------------------常量定义*/ inline void File() { freopen("track.in", "r", stdin); freopen("track.out", "w", stdout); } /*----------------------------------------文件*/ int n, m, du[A], f[A], g[A], ans, maxdu, sum, cnt, q[A], K, dp[A], vis[A]; struct node {int u, v, w;} a[A]; struct edge {int v, w, nxt;} e[A << 1]; int head[A], ecnt; /*------------------------------------变量定义*/ inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();} while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();} return x * f; } /*----------------------------------------快读*/ void add_edge(int u, int v, int w) {e[++ecnt] = (edge){v, w, head[u]}; head[u] = ecnt;} void dfs1(int u, int pre) { for(int i = head[u]; i; i = e[i].nxt) { int v = e[i].v, w = e[i].w; if(v == pre) continue; dfs1(v, u); if(f[v] + w > f[u]) g[u] = f[u], f[u] = f[v] + w; else if(f[v] + w > g[u]) g[u] = f[v] + w; } ans = Max(ans, f[u] + g[u]); } void work1() { dfs1(1, 0); printf("%lld", ans); } void dfs2(int u, int pre, int _s, int x) { if(_s >= x) cnt++, _s = 0; for(int i = head[u]; i; i = e[i].nxt) { int v = e[i].v, w = e[i].w; if(v == pre) continue; dfs2(v, u, _s + w, x); } } bool check2(int x) { cnt = 0; dfs2(1, 0, 0, x); return cnt >= m; } void work2() { int l = 0, r = sum; while(l <= r) { int mid = l + r >> 1; if(check2(mid)) l = mid + 1, ans = mid; else r = mid - 1; } printf("%lld", ans); } bool cmp(node x, node y) {return x.w > y.w;} bool check3(int x) { cnt = 0; for(int i = 1, j = n - 1; i <= j; i++) { if(a[i].w >= x) cnt++; else { if(i == j) break; while(a[i].w + a[j].w < x && i < j - 1) j--; if(a[i].w + a[j].w >= x) cnt++, j--; else break; } } return cnt >= m; } void work3() { std::sort(a + 1, a + n, cmp); int l = 0, r = sum; while(l <= r) { int mid = l + r >> 1; if(check3(mid)) l = mid + 1, ans = mid; else r = mid - 1; } printf("%lld", ans); } void dfs(int u, int pre, int x) { for(int i = head[u], v = e[i].v; i; i = e[i].nxt, v = e[i].v) if(v != pre) dfs(v, u, x); K = 0; dp[u] = 0; for(int i = head[u], v = e[i].v; i; i = e[i].nxt, v = e[i].v) if(v != pre) q[++K] = dp[v] + e[i].w; std::sort(q + 1, q + 1 + K); for(int i = K; i && q[i] >= x; i--) cnt++, K--; for(int i = 1; i <= K; i++) if(vis[i] != u) { int L = i + 1, R = K, pos = K + 1; while(L <= R) { int Mid = L + R >> 1; if(q[i] + q[Mid] >= x) pos = Mid, R = Mid - 1; else L = Mid + 1; } while(vis[pos] == u && pos <= K) pos++; if(pos <= K) vis[i] = vis[pos] = u, cnt++; } for(int i = K; i; i--) if(vis[i] != u) {dp[u] = q[i]; break;} } bool check(int x) { memset(vis, 0, sizeof vis); cnt = 0; dfs(1, 0, x); return cnt >= m; } void work() { int l = 0, r = sum; while(l <= r) { int mid = l + r >> 1; if(check(mid)) l = mid + 1, ans = mid; else r = mid - 1; } printf("%lld", ans); } /*----------------------------------------函数*/ signed main() { File(); n = read(); m = read(); for(int i = 1; i < n; i++) { int x = read(), y = read(), z = read(); a[i] = (node){x, y, z}; sum += z; du[x]++; du[y]++; maxdu = Max(maxdu, Max(du[x], du[y])); add_edge(x, y, z); add_edge(y, x, z); } if(m == 1) work1(); else if(maxdu == 2) work2(); else if(maxdu == n - 1) work3(); else work(); return 0; } /* 最小最大 二分答案 先扔了 看数据 m = 1 树的直径 20 7 1 1 2 10 1 3 5 2 4 9 2 5 8 3 6 6 3 7 7 链 20 每条赛道一定是连续的一段 二分答案 进行check 二分一个最小值 8 x 1 2 7 3 4 2 2 3 3 7 8 1 6 7 4 5 6 5 4 5 6 过小样例 应该差不多 菊花图 15 一条赛道最多只能有两条边 二分答案 进行check 按边权排序 双指针 贪 10 x 1 3 6 1 10 1 1 2 8 1 4 9 1 9 7 1 5 3 1 6 4 1 7 2 1 8 8 过小样例 跑路 大概能过前 11 个点 二叉树 25 12 - 16 没思路 感觉这个已经接近正解了 尝试直接开正解 一棵树 最多只能有一条边连出去 贪一下 能在子树内连的尽量在子树内连 尝试维护唯一一条连出去的边 一定是除却已经连接的边的最长的边 父节点中 用所有儿子中连出的这条边进行相互匹配 排序在匹配复杂度爆炸 写不出来 弃了 卒 */