【题录】Atcoder KEYENCE 2020
C.
如果S不等于1e9的话,可以直接放上k个S,再把剩下的所有数都写成1e9。如果S等于1e9,那么可以把剩下所有的数都写成1;
#include <bits/stdc++.h> using namespace std; #define MAXX 1000000000 int n, S, K; int read() { int x = 0, k = 1; char c; c = getchar(); while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); } while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * k; } int main() { n = read(), K = read(), S = read(); if(n == K) { for(int i = 1; i < K; i ++) printf("%d ", S); printf("%d\n", S); return 0; } if(S == MAXX) { for(int i = 1; i <= K; i ++) printf("%d ", S); for(int i = K + 1; i < n; i ++) printf("1 "); puts("1"); } else { for(int i = 1; i <= K; i ++) printf("%d ", S); for(int i = K + 1; i < n; i ++) printf("%d ", S + 1); printf("%d\n", S + 1); } return 0; }
D.
不难发现一张牌被翻动的次数的奇偶性只与卡牌的始末位置相关,而总共被翻动的次数即为最后卡牌序列的逆序对数。证明:1.翻动的次数至少为逆序对数:如果两张牌未被翻动,则二者的相对位置一直不会改变,所以每一对逆序对卡牌至少要被翻动一次。2.翻动的次数可以达到最优解:可以通过每次将当前序列需要移动到第一张的卡牌不断向前翻动直到第一张来实现。所以可以选择用状压dp,从前向后构造最终的卡牌序列,令dp[st][h] 表示st(压缩状态)中所有为1的卡牌已经放在了前面的若干个位置后,上一个位置的值为h(离散化后)的最少逆序对个数。
#include <bits/stdc++.h> using namespace std; #define maxn 270000 #define N 50 #define INF 99999999 int n, ans = INF, bits[maxn], a[maxn], b[maxn], P[maxn], dp[maxn][N]; int tot1, tot, cnt[maxn][N], num[N][N], dig[maxn]; map <int, int> Map; int read() { int x = 0, k = 1; char c; c = getchar(); while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); } while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * k; } void Down(int &x, int y) { if(x > y) x = y; } int Int_abs(int x) { if(x < 0) x = -x; return x; } void Pre() { for(int i = 0; i < bits[n]; i ++) { int temp = i; while(temp) { dig[i] += temp & 1; temp >>= 1; } for(int j = 1; j <= n; j ++) { if(bits[j - 1] & i) continue; int t = i; for(int k = 1; k <= j; k ++) t >>= 1; while(t) { cnt[i][j] += (t & 1); t >>= 1; } } } } void pt(int x) { int d[20], t = 0; for(int i = 0; i <= n; i ++) d[i] = 0; while(x) { d[++ t] = x & 1; x >>= 1; } for(int i = 1; i <= n; i ++) cout << d[i]; } void DP() { for(int i = 0; i < bits[n]; i ++) for(int j = 0; j <= tot1; j ++) dp[i][j] = INF; dp[0][0] = 0; for(int i = 0; i < bits[n]; i ++) { for(int j = 0; j <= tot1; j ++) { if(dp[i][j] == INF) continue; for(int k = 1; k <= n; k ++) { if(bits[k - 1] & i) continue; int t = num[k][dig[i] + 1]; if(t >= j) Down(dp[i | bits[k - 1]][t], dp[i][j] + cnt[i][k]); } } } } int main() { n = read(); bits[0] = 1; for(int i = 1; i <= n; i ++) bits[i] = bits[i - 1] << 1; for(int i = 1; i <= n; i ++) a[i] = read(), P[++ tot] = a[i]; for(int i = 1; i <= n; i ++) b[i] = read(), P[++ tot] = b[i]; sort(P + 1, P + 1 + tot); for(int i = 1; i <= tot; i ++) if(P[i] != P[i - 1]) Map[P[i]] = ++ tot1; for(int i = 1; i <= n; i ++) for(int j = 1; j <= n; j ++) num[i][j] = (Int_abs(i - j) & 1) ? Map[b[i]] : Map[a[i]]; Pre(); DP(); for(int i = 1; i <= tot1; i ++) ans = min(ans, dp[bits[n] - 1][i]); if(ans < INF) printf("%d\n", ans); else printf("-1\n"); return 0; }
E.
考虑一个点到达异色点的最短路径,则要么该点与异色点直接相连,边权为d,要么该点与一个d小于自己的同色点相连,边权为二者差值。又注意到最短的d一定是直接和异色点相连产生的,因此我们每次找到最短的d的点,将所有与其有连边的点相连,之后这些点再去与d大于自己的点相连。这样构造到最后即可判断是否能够。如果有从头到尾没有涉及的边,只要将边的权值设为1e9即可视为删除此边。
#include <bits/stdc++.h> using namespace std; #define maxn 1000000 #define INF 1000000000 int n, m, d[maxn], w[maxn], mark[maxn], col[maxn]; bool flag = 1; queue <int> q; int read() { int x = 0, k = 1; char c; c = getchar(); while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); } while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * k; } struct edge { int cnp = 1, head[maxn], id[maxn], last[maxn], to[maxn]; void add(int u, int v, int c) { to[cnp] = v, id[cnp] = c, last[cnp] = head[u], head[u] = cnp ++; } }E; struct node { int u, d; node(int _u, int _d) { u = _u, d = _d; } friend bool operator >(node a, node b) { return a.d > b.d; } }; struct heap { priority_queue <node, vector <node>, greater <node> > h; int size = 0; node top() { while(h.size() && mark[h.top().u]) h.pop(); if(!h.size()) return node(-1, -1); return h.top(); } void push(node a) { h.push(a); size ++; } void pop() { if(h.size()) h.pop(); } }H; bool Solve() { int SW = H.top().u; H.pop(); H.size --; if(SW == -1) return 0; int FB = 0; col[SW] = 1; mark[SW] = 1; for(int i = E.head[SW]; i; i = E.last[i]) { int v = E.to[i]; if(d[v] != d[SW]) continue; FB = v; w[E.id[i]] = d[v]; } //cout << "&&&&" << SW << " " << FB << endl; if(!FB) return 0; for(int i = E.head[SW]; i; i = E.last[i]) { int v = E.to[i]; if(mark[v]) continue; q.push(v); w[E.id[i]] = d[v]; mark[v] = 1; col[v] = 2; H.size --; } while(!q.empty()) { int u = q.front(); q.pop(); for(int i = E.head[u]; i; i = E.last[i]) { int v = E.to[i]; if(mark[v] || d[v] == d[u]) continue; q.push(v); w[E.id[i]] = d[v] - d[u]; col[v] = 2; H.size --; mark[v] = 1; } } return 1; } int main() { n = read(), m = read(); for(int i = 1; i <= n; i ++) d[i] = read(); for(int i = 1; i <= m; i ++) { int u = read(), v = read(); if(d[v] >= d[u]) E.add(u, v, i); if(d[u] >= d[v]) E.add(v, u, i); } for(int i = 1; i <= n; i ++) H.push(node(i, d[i])); while(H.size >= 1 && (flag = Solve())); if(H.size >= 1 || !flag) printf("-1\n"); else { for(int i = 1; i <= n; i ++) if(col[i] == 1) printf("W"); else printf("B"); puts(""); for(int i = 1; i <= m; i ++) if(w[i]) printf("%d\n", w[i]); else printf("%d\n", INF); } return 0; }