CCPC-Wannafly Summer Camp 2019 Day1
A - Jzzhu and Cities
题意:n座城市,m条路,k条铁路啥的吧,然后要求最多能删多少条铁路保持1到$n$的最短路不变。
思路:因为铁路是从1出发的。所以能删的铁路有该铁路长度不等于1到该节点的最短路的,相等的时候,如果该节点的入度非1,也可以删去。
#include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <queue> #include <functional> #define ll long long #define pb push_back #define P pair<ll, int> using namespace std; template<typename T> inline void read(T &x) { x = 0; T f = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); } x *= f; } const ll INF = 0x3f3f3f3f3f3f3f3f; const int N = 1e5 + 7; vector<P> G[N]; bool done[N]; struct Po { int v; ll c; } po[N]; int n, m, k; int in[N]; ll d[N]; void dijkstra(ll *d, int s) { for (int i = 1; i <= n; i++) d[i] = INF, done[i] = 0; d[s] = 0; priority_queue<P, vector<P>, greater<P> > que; que.push(P(0, s)); while (!que.empty()) { auto p = que.top(); que.pop(); int u = p.second; if (done[u]) continue; done[u] = 1; for (auto pp: G[u]) { int v = pp.second; if (d[v] > d[u] + pp.first) { in[v] = 1; d[v] = d[u] + pp.first; que.push(P(d[v], v)); } else if (d[v] == d[u] + pp.first) { in[v]++; } } } } int main() { read(n); read(m); read(k); while (m--) { int u, v; ll c; read(u); read(v); read(c); G[u].pb(P(c, v)); G[v].pb(P(c, u)); } for (int i = 1; i <= k; i++) { read(po[i].v); read(po[i].c); G[1].pb(P(po[i].c, po[i].v)); G[po[i].v].pb(P(po[i].c, 1)); } dijkstra(d, 1); memset(done, 0, sizeof(done)); int ans = 0; for (int i = 1; i <= k; i++) { int v = po[i].v; if (d[v] < po[i].c) ans++; else if (d[v] == po[i].c && in[v] > 1) { in[v]--; ans++; } } printf("%d\n", ans); return 0; }
B - Phillip and Trains
题意:$3 \times n$的方格,一个人在最左边的一个起始位置,先向右走一步,再选择向上、不动、向下走一步。然后轮到所有火车往左走两步。问这个人能否安全走到最右边那列。
思路:BFS。当前位置先往右走一步,再枚举上中下三个位置,因为火车向左走两步,可以等同于人往右走两步,所以就是判断人能不能往后走两格。
#include <cstdio> #include <algorithm> #include <cctype> #include <queue> #include <cstring> #define P pair<int, int> using namespace std; const int N = 110; int n, k; char s[5][N]; bool ans; bool mp[5][N]; bool bfs(int sx) { queue<pair<int, int> > que; que.push(P(sx, 1)); while (!que.empty()) { P p = que.front(); que.pop(); int x = p.first, y = p.second; if (isalpha(s[x][++y])) continue; if (y >= n) return true; for (int i = -1; i <= 1; i++) { int dx = x + i; if (dx <= 0 || dx > 3) continue; if (isalpha(s[dx][y]) || isalpha(s[dx][y + 1]) || isalpha(s[dx][y + 2]) || s[dx][y + 2] == 1) continue; int dy = y + 2; if (dy >= n) return true; s[dx][dy] = 1; que.push(P(dx, dy)); } } return false; } int main() { int T; scanf("%d", &T); while (T--) { scanf("%d%d", &n, &k); memset(s, 0, sizeof(s)); for (int i = 1; i <= 3; i++) scanf("%s", s[i] + 1); int sx = 0; for (int i = 1; i <= 3; i++) if (s[i][1] == 's') sx = i; if (bfs(sx)) puts("YES"); else puts("NO"); } return 0; }
C - A Mist of Florescence
借鉴的别人的思路。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; char s[55][55]; int main() { int a, b, c, d; scanf("%d%d%d%d", &a, &b, &c, &d); a--; b--; for (int i = 0; i < 50; i++) for (int j = 0; j < 50; j++) if (i < 25) s[i][j] = 'A'; else s[i][j] = 'B'; for (int i = 0; i < 24; i += 2) for (int j = 0; j < 50; j += 2) { if (b) s[i][j] = 'B', b--; else if (d) s[i][j] = 'D', d--; } for (int i = 26; i < 50; i += 2) for (int j = 0; j < 50; j += 2) { if (a) s[i][j] = 'A', a--; else if (c) s[i][j] = 'C', c--; } printf("50 50\n"); for (int i = 0; i < 50; i++) puts(s[i]); return 0; }
D - Unbearable Controversy of Being
题意:给一个有向图,找出$\left( a,b,c,d\right)$的对数满足题面的图。
思路:暴力枚举$a$,$c$然后组合数搞搞。
#include <cstdio> #include <algorithm> #include <vector> using namespace std; const int N = 3031; bool mp[N][N]; vector<int> G[N]; template<typename T> inline void read(T &x) { x = 0; T f = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); } x *= f; } int main() { //freopen("in.txt", "r", stdin); int n, m; read(n); read(m); while (m--) { int u, v; read(u); read(v); mp[u][v] = 1; G[u].push_back(v); } int ans = 0; for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) if (i != j) { int res = 0; for (auto temp: G[i]) if (temp != i && temp != j && mp[temp][j]) res++; ans += res * (res - 1) / 2; } printf("%d\n", ans); return 0; } /* input 5 4 1 2 2 3 1 4 4 3 output 1 input 4 12 1 2 1 3 1 4 2 1 2 3 2 4 3 1 3 2 3 4 4 1 4 2 4 3 output 12 */
E - Igor In the Museum
#include <cstdio> #include <algorithm> #include <map> #include <cstring> #define P pair<int, int> using namespace std; const int N = 1010; bool vis[N][N]; int cnt, n, m; int ans[N * N]; int mp[N][N]; char mpp[N][N]; const int dir[4][2] = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}}; void dfs(int x, int y, int now) { vis[x][y] = 1; mp[x][y] = now; for (int i = 0; i < 4; i++) { int dx = x + dir[i][0], dy = y + dir[i][1]; if (dx <= 0 || dy <= 0 || dx > n || dy > m) continue; if (!vis[dx][dy]) { if (mpp[dx][dy] == '*') ans[now]++; else dfs(dx, dy, now); } } } int main() { int k; scanf("%d%d%d", &n, &m, &k); for (int i = 1; i <= n; i++) scanf("%s", mpp[i] + 1); for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { if (mpp[i][j] == '.' && !vis[i][j]) { dfs(i, j, ++cnt); } } while (k--) { int x, y; scanf("%d%d", &x, &y); printf("%d\n", ans[mp[x][y]]); } return 0; }
F - The Child and Toy
贪心优先处理权值大的。只考虑两个的情况,优先处理一个会使答案加上另一个的权值,所以先处理大的会比处理小的更优。这个能推广到多个数。
#include <cstdio> #include <algorithm> #include <vector> #define ll long long #define pb push_back using namespace std; template<typename T> inline void read(T &x) { x = 0; T f = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); } x *= f; } const int N = 1010; struct P { ll v; int u; bool operator < (const P &rhs) const { return v > rhs.v; } } p[N]; int n, a[N], m; bool vis[N]; vector<int> G[N]; int main() { read(n); read(m); for (int i = 1; i <= n; i++) { read(a[i]); p[i].u = i; p[i].v = a[i]; } while (m--) { int u, v; read(u); read(v); G[u].pb(v); G[v].pb(u); } sort(p + 1, p + n + 1); ll ans = 0; for (int i = 1; i <= n; i++) { int u = p[i].u; vis[u] = 1; for (auto v: G[u]) if (!vis[v]) ans += a[v]; } printf("%lld\n", ans); return 0; }
G - New Year Permutation
题意:给一个排列,以及代表哪些位置能交换的矩阵,问字典序最小的排列长啥样。
思路:现在一想不就是Floyd传递闭包吗。写的时候没想起来,就dfs处理一下连通块之类的。然后就可以贪心去排了
#include <cstdio> #include <algorithm> using namespace std; template<typename T> inline void read(T &x) { x = 0; T f = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); } x *= f; } const int N = 330; int pos[N], a[N]; char s[N][N]; int mp[N][N]; int block[N], n, cnt; void dfs(int u, int c) { //printf("%d %d\n", u, c); block[u] = c; for (int i = 1; i <= n; i++) { if (s[u][i] == '1' && !block[i]) { dfs(i, c); } } } int main() { //freopen("in.txt", "r", stdin); read(n); for (int i = 1; i <= n; i++) { read(a[i]); pos[a[i]] = i; } for (int i = 1; i <= n; i++) scanf("%s", s[i] + 1); for (int i = 1; i <= n; i++) if (!block[i]) dfs(i, ++cnt); for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { mp[i][j] = (block[i] == block[j]) ? 1 : 0; } } for (int i = n; i > 1; i--) { for (int j = n; j > pos[i]; j--) { if (mp[pos[i]][j] && a[j] < i) { //printf("%d %d\n", pos[i], j); int temp = a[j]; swap(a[pos[i]], a[j]); swap(pos[i], pos[temp]); break; } } } for (int i = 1; i <= n; i++) printf("%d%c", a[i], " \n"[i == n]); return 0; } /* input 7 5 2 4 3 6 7 1 0001001 0000000 0000010 1000001 0000000 0010000 1001000 output 1 2 4 3 6 7 5 input 5 4 2 1 5 3 00100 00011 10010 01101 01010 output 1 2 3 4 5 */
H - Alyona and the Tree
题意:给一棵树,如果存在一个叶子到它的一个祖先的距离大于它的权值,则该叶子应该删去,问最少需要删多少节点。
思路:两遍dfs,第一次处理出每个节点子树的节点数、该节点到1的距离$d$和从1到该节点的前缀距离中的最小值$mn$,$d-mn$能得到该节点到所有祖先中的最大距离,然后在dfs判断当前该节点是否要删即可。
#include <cstdio> #include <algorithm> #include <vector> #define ll long long #define P pair<int, ll> #define pb push_back using namespace std; template<typename T> inline void read(T &x) { x = 0; T f = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); } x *= f; } const int N = 1e5 + 7; ll dis[N], a[N], mm[N]; int n, sz[N], ans; vector<P> G[N]; void dfs1(int u, int pre) { sz[u] = 1; for (int i = 0; i < G[u].size(); i++) { P p = G[u][i]; if (p.first == pre) continue; dis[p.first] = dis[u] + p.second; mm[p.first] = min(mm[u], dis[p.first]); dfs1(p.first, u); sz[u] += sz[p.first]; } } void dfs2(int u, int pre) { for (int i = 0; i < G[u].size(); i++) { P p = G[u][i]; if (p.first == pre) continue; if (dis[p.first] - mm[p.first] > a[p.first]) { ans += sz[p.first]; continue; } dfs2(p.first, u); } } int main() { read(n); for (int i = 1; i <= n; i++) read(a[i]); for (int i = 2; i <= n; i++) { int u; ll c; read(u); read(c); G[u].pb(P(i, c)); G[i].pb(P(u, c)); } dfs1(1, 1); dfs2(1, 1); printf("%d\n", ans); }
I - Network Safety
思路自https://www.cnblogs.com/DeaphetS/p/9599587.html
J - Resort
思路:写了个类似于记忆化的东西,懒得想太多了。
#include <cstdio> #include <algorithm> #include <vector> #include <cstring> using namespace std; template<typename T> inline void read(T &x) { x = 0; T f = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); } x *= f; } const int N = 1e5 + 10; int dp[N], a[N], to[N]; bool is[N]; int top, st[N], out[N]; vector<int> G[N]; int DP(int u) { if (dp[u] != -1) return dp[u]; int &ans = dp[u]; ans = 1; int v = to[u]; if (v && !is[v] && out[v] == 1) ans = DP(v) + 1; return ans; } int main() { //freopen("in.txt", "r", stdin); memset(dp, -1, sizeof(dp)); int n; read(n); int cnt = 0; for (int i = 1; i <= n; i++) { int x; read(x); if (x) a[++cnt] = i, is[i] = 1; } //for (int i = 1; i <= cnt; i++) printf("%d%c", a[i], " \n"[i == cnt]); for (int i = 1; i <= n; i++) { int x; read(x); if (!x) continue; out[x]++; to[i] = x; } int ans = 0, id = 0; for (int i = 1; i <= cnt; i++) { int res = DP(a[i]); if (res > ans) { id = a[i]; ans = res; } } printf("%d\n", ans); st[++top] = id; id = to[id]; for (; id && !is[id] && out[id] == 1; id = to[id]) st[++top] = id; while(top) printf("%d%c", st[top], " \n"[top == 1]), --top; } /* Input 5 0 0 0 0 1 0 1 2 3 4 Output 5 1 2 3 4 5 Input 5 0 0 1 0 1 0 1 2 2 4 Output 2 4 5 Input 4 1 0 0 0 2 3 4 2 Output 1 1 */
K - Royal Questions
题意:王子和公主的二分图匹配,求最大收
思路:数据范围小可以二分图匹配搞搞,但是这数据范围太大了。注意到一个公主与两个王子连边的权值是一样大的,就可以不考虑这个公主了,只考虑两个王子之间有一条边。
魔改一下kruskal,边按权值从大到小排序,只有当两个点都被选中过的时候,才不能继续选,两个点在一个并查集里同理。
#include <cstdio> #include <algorithm> using namespace std; template<typename T> inline void read(T &x) { x = 0; T f = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); } x *= f; } const int N = 2e5 + 10; struct E { int a, b, w; bool operator < (const E &rhs) const { return w > rhs.w; } } e[N]; int fa[N], p[N]; int getfa(int x) { return x == fa[x] ? x : fa[x] = getfa(fa[x]); } int main() { //freopen("in.txt", "r", stdin); int n, m; read(n); read(m); for (int i = 1; i <= m; i++) { read(e[i].a); read(e[i].b); read(e[i].w); } sort(e + 1, e + m + 1); for (int i = 1; i <= n; i++) fa[i] = i; int ans = 0; for (int i = 1; i <= m; i++) { int x = getfa(e[i].a), y = getfa(e[i].b); if (x != y) { if (p[x] && p[y]) continue; p[x] = p[x] | p[y]; fa[y] = x; ans += e[i].w; } else if (!p[x]) { p[x] = 1; ans += e[i].w; } } printf("%d\n", ans); return 0; }
L - Love Triangle
#include <cstdio> #include <vector> #include <queue> #define eb emplace_back using namespace std; const int N = 5050; int to[N]; int main() { int n; scanf("%d", &n); for (int i = 1; i <= n; i++) { int x; scanf("%d", &x); to[i] = x; } for (int i = 1; i <= n; i++) { if (to[to[to[i]]] == i) { puts("YES"); return 0; } } puts("NO"); return 0; }
M - Bakery
题意:$n$座城市,$m$条边,$k$个关键点,问能否找一个非关键点和一个关键点,它们之间的距离最小。
思路:刚开始以为要$k$次最短路???过一会才意识过来,这个最小距离只能是题目给的边。然后找一下就OK了。
#include <cstdio> #include <algorithm> using namespace std; template<typename T> inline void read(T &x) { x = 0; T f = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); } x *= f; } const int N = 1e5 + 7; const int INF = 0x3f3f3f3f; bool pos[N]; struct P { int u, v, c; } p[N]; int main() { int n, m, k; read(n); read(m); read(k); for (int i = 1; i <= m; i++) { read(p[i].u); read(p[i].v); read(p[i].c); } for (int i = 1; i <= k; i++) { int x; read(x); pos[x] = 1; } int ans = INF; for (int i = 1; i <= m; i++) if (pos[p[i].u] + pos[p[i].v] == 1) ans = min(ans, p[i].c); if (ans == INF) ans = -1; printf("%d\n", ans); return 0; }
N - News Distribution
思路:又是一道被我误伤成dfs的题,但貌似我的代码跑得更快?hhh
#include <cstdio> #include <vector> #define pb push_back using namespace std; const int N = 1e6 + 10; vector<int> G[N]; int vis[N], cnt, ans[N], n; void dfs(int u) { vis[u] = cnt; if (u <= n) ans[cnt]++; for (auto v: G[u]) { if (!vis[v]) { dfs(v); } } } int main() { int m; scanf("%d%d", &n, &m); for (int i = 1; i <= m; i++) { int k; scanf("%d", &k); while (k--) { int x; scanf("%d", &x); G[x].pb(i + n); G[i + n].pb(x); } } for (int i = 1; i <= n; i++) { if (!vis[i]) { cnt++; dfs(i); } printf("%d%c", ans[vis[i]], " \n"[i == n]); } return 0; }
O - NP-Hard Problem
思路:二分图染色
#include <cstdio> #include <cstring> #include <algorithm> #include <vector> #define eb emplace_back using namespace std; template<typename T> inline void read(T &x) { x = 0; T f = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); } x *= f; } const int N = 1e5 + 7; int n, m, color[N]; vector<int> G[N], ans[2]; bool dfs(int u, int c) { color[u] = c; ans[c == 1].eb(u); for (auto v: G[u]) { if (!color[v] && !dfs(v, -c)) return false; else if (color[v] == color[u]) return false; } return true; } int main() { read(n); read(m); while (m--) { int u, v; read(u); read(v); G[u].eb(v); G[v].eb(u); } bool res = 1; for (int i = 1; i <= n; i++) { if (!color[i]) { res = dfs(i, 1); if (!res) { puts("-1"); return 0; } } } int sz = ans[1].size(); printf("%d\n", sz); for (int i = 0; i < sz; i++) printf("%d%c", ans[1][i], " \n"[i == sz - 1]); sz = ans[0].size(); printf("%d\n", sz); for (int i = 0; i < sz; i++) printf("%d%c", ans[0][i], " \n"[i == sz - 1]); return 0; }