A - Space Navigation
题意:
有一个操作序列,会按照序列上的执行,现在要求任意删去一些操作,使得最终能从((0,0))走到((x,y)),询问是否可行。
题解:
能走到的地方是一个范围,判断是否在范围内即可。
#include <bits/stdc++.h> #define Mid ((l + r) >> 1) #define lson (rt << 1) #define rson (rt << 1 | 1) using namespace std; int read(){ char c; int num, f = 1; while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0'; while(c = getchar(), isdigit(c)) num = num * 10 + c - '0'; return f * num; } int x, y, u, d, r, l, n; char s[1000009]; void work() { x = read(); y = read(); scanf("%s", s + 1); n = strlen(s + 1); u = d = r = l = 0; for(int i = 1; i <= n; i++) { if(s[i] == 'U') u++; if(s[i] == 'D') d++; if(s[i] == 'R') r++; if(s[i] == 'L') l++; } int f = 1; if(x < 0) { f &= (l >= -x); } else f &= (r >= x); if(y < 0) { f &= (d >= -y); } else f &= (u >= y); printf("%s ", f ? "YES" : "NO"); } signed main() { int Case = read(); while(Case--) work(); return 0; }
B - New Colony
题意:
从1号位置不停放球,球会在第一个坡处卡住,然后使坡之前的位置高度加一,求第(k)个球在哪里停下。
题解:
单调栈。
栈单调递减, 每次进栈一个新的高度,不断弹掉栈顶比他矮的,计算对k的贡献。
最大贡献就是这两个进栈的位置的前一个到栈中第二个元素后一个位置的距离乘上最大可以增加的高度。
如果贡献大于这个(k)就输出位置,位置可以对长度取模后直接计算出。
#include <bits/stdc++.h> #define int long long #define Mid ((l + r) >> 1) #define lson (rt << 1) #define rson (rt << 1 | 1) using namespace std; int read(){ char c; int num, f = 1; while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0'; while(c = getchar(), isdigit(c)) num = num * 10 + c - '0'; return f * num; } int n, k, h[109], s[100009], t; void work() { n = read(); k = read(); t = 0; for(int i = 1; i <= n; i++) h[i] = read(); s[0] = 0; s[++t] = 1; h[0] = 0x3f3f3f3f; for(int i = 2; i <= n; i++) { while(t && h[s[t]] <= h[i]) { int hi = min(h[s[t - 1]], h[i]) - h[s[t]]; //cout << i << " " << ((i - 1) - s[t - 1]) << endl; if(hi * ((i - 1) - s[t - 1]) >= k) { k %= ((i - 1) - s[t - 1]); if(k == 0) { printf("%lld ", s[t - 1] + 1); } else printf("%lld ", (i - 1) - k + 1); //printf("%d ", (i - 1) - k); return ; } else k -= hi * ((i - 1) - s[t - 1]); t--; } s[++t] = i; //cout << i << " " << k << endl; //cout << k << endl; } printf("-1 "); } signed main() { int Case = read(); while(Case--) work(); return 0; }
C - Fence Painting
题意:
有一堆栅栏,初始颜色给出,现在想把他们刷成目标颜色,有m个画家依次去刷,每个画家只能把一个栅栏刷成(c[i])颜色,求安排画家达成目标,或者达不到目标。
题解:
时间倒流法,每一个栅栏的颜色只跟最后一个画家刷的颜色有关。
倒序去确定每个画家要刷的栅栏。
特殊处理的有最后一个画家。
防止画家不够,先看看他能不能把一个初始颜色不等于目标颜色的栅栏刷成目标颜色,不行的话再看看是否存在一个初始颜色等于目标颜色的栅栏。
确定了最后一个画家刷的栅栏后,其他的画家假如没有可以画的东西,都在这个画家的栅栏上画即可。
可以用set维护剩余每个颜色需要刷的位置。
#include <bits/stdc++.h> #define int long long #define Mid ((l + r) >> 1) #define lson (rt << 1) #define rson (rt << 1 | 1) using namespace std; int read(){ char c; int num, f = 1; while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0'; while(c = getchar(), isdigit(c)) num = num * 10 + c - '0'; return f * num; } const int N = 2e5 + 1009; int n, m, a[N], b[N], c[N], vis[N], p, pos[N]; set<int> S[N]; void work() { n = read(); m = read(); for(int i = 1; i <= n; i++) a[i] = read(); for(int i = 1; i <= n; i++) b[i] = read(); for(int i = 1; i <= m; i++) c[i] = read(); for(int i = 1; i <= n; i++) S[i].clear(); int f = 0; for(int i = 1; i <= n; i++) { if(a[i] != b[i] && b[i] == c[m]) { f = 1; pos[m] = i; break; } } if(f == 0) { for(int i = 1; i <= n; i++) { if(a[i] == b[i] && a[i] == c[m]) { pos[m] = i; f = 1; break; } } } if(f == 0) { printf("NO "); return ; } for(int i = 1; i <= n; i++) if(i != pos[m] && a[i] != b[i]) S[b[i]].insert(i); for(int i = m - 1; i; i--) { if(S[c[i]].empty()) { pos[i] = pos[m]; } else { pos[i] = *S[c[i]].begin(); S[c[i]].erase(S[c[i]].begin()); } } for(int i = 1; i <= n; i++) { if(!S[i].empty()) { printf("NO "); return ; } } printf("YES "); for(int i = 1; i <= m; i++) printf("%d ", pos[i]); printf(" "); } signed main() { int Case = read(); while(Case--) work(); return 0; }
D - AB Graph
题意:
一张完全有向图,每条边标有a或者b,要求找出一条长度为m的路径,使得路径经过的标记序列构成回文串。
题解:
首先如果m是奇数的话,在1,2之间走来走去就行了。
如果m是偶数,首先判断是否存在一对点,使得来回的标记都相同。
相同的话直接来回走就行。
如果没有,则说明每一对点来回的标记都是a,b。
观察3个点组成的子图。
发现一定存在一条链,使得链中间的点到两边的点的边上的标号不同,即:
(1->2=a,2->1=b,2->3=a,3->2=b)。
有这样的路径之后假如m是4的倍数,就都可以构造出了,只需要从中间点出发,往左边走(m/4)个来回,再往右边走(m/4)个来回。
假如m是2的倍数,考虑把一对(a,a)拆开放在整个序列的两边,即先从1走到2,然后再经过上述变换,最后从2走到3。
#include <bits/stdc++.h> #define Mid ((l + r) >> 1) #define lson (rt << 1) #define rson (rt << 1 | 1) using namespace std; int read(){ char c; int num, f = 1; while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0'; while(c = getchar(), isdigit(c)) num = num * 10 + c - '0'; return f * num; } int n, k, g[1009][1009]; char c[10009]; void work() { n = read(); k = read(); for(int i = 1; i <= n; i++) { scanf("%s", c + 1); for(int j = 1; j <= n; j++) if(i != j) { g[i][j] = c[j] == 'a'; } } if(k & 1) { printf("YES "); for(int i = 1; i <= k + 1; i++) printf("%d ", (i & 1) + 1); printf(" "); return ; } for(int i = 1; i <= n; i++) { for(int j = i + 1; j <= n; j++) { if(g[i][j] == g[j][i]) { printf("YES "); for(int q = 1; q <= k + 1; q++) printf("%d ", (q & 1) ? i : j); printf(" "); return ; } } } if(n <= 2) { printf("NO "); return ; } printf("YES "); int a, b, c; if(g[1][2] != g[1][3]) a = 1, b = 2, c = 3; if(g[2][1] != g[2][3]) a = 2, b = 1, c = 3; if(g[3][1] != g[3][2]) a = 3, b = 1, c = 2; if(k % 4 == 0) { printf("%d ", a); for(int i = 1; i <= k / 2; i++) printf("%d ", i & 1 ? b : a); for(int i = 1; i <= k / 2; i++) printf("%d ", i & 1 ? c : a); printf(" "); } else { printf("%d %d ", b, a); k -= 2; for(int i = 1; i <= k / 2; i++) printf("%d ", i & 1 ? c : a); for(int i = 1; i <= k / 2; i++) printf("%d ", i & 1 ? b : a); printf("%d ", c); printf(" "); } } signed main() { int Case = read(); while(Case--) work(); return 0; }
E - Sorting Books
题意:
一组序列,每次可以移动一个元素到序列最右边,问把所有相同元素全部移到相邻最少需要移动几次。
题解:
很有原题的味道啊?
不过反正考场上我是想不出来。
(dp[i])表示后缀([i,n])中移动所有元素相邻最少保留的元素数量。
注意这个(dp[i])还需要考虑前面的元素还能继续加入。
(f[i])表示i元素有多少个,(cf[i])表示当前后缀中i元素有多少个,(l[i],r[i])表示i元素第一次,最后一次出现的位置。
考虑我们有两种方式达成目标。
- 移动(a[i])使他跟后面的元素合并。
- 不移动(a[i])但是最左端的(a[i])一定要作为第一个(a[i])出现的位置。
转移就是:
(dp[i] = dp[i + 1])
(dp[i] = cf[a[i]])
但是当(l_{a[i]}==i)时,有额外的转移:(dp[i] = f[a[i]] + dp[r[a[i]]+1])
记得取max。
#include <bits/stdc++.h> #define Mid ((l + r) >> 1) #define lson (rt << 1) #define rson (rt << 1 | 1) using namespace std; int read(){ char c; int num, f = 1; while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0'; while(c = getchar(), isdigit(c)) num = num * 10 + c - '0'; return f * num; } const int N = 5e5 + 1009; int n, m, f[N], cf[N], dp[N], l[N], r[N], a[N]; signed main() { n = read(); for(int i = 1; i <= n; i++) { int x = read(); a[i] = x; if(l[x] == 0) l[x] = i; r[x] = i; f[x]++; } for(int i = n; i; i--) { dp[i] = dp[i + 1]; cf[a[i]]++; if(l[a[i]] == i) dp[i] = max(dp[i], dp[r[a[i]] + 1] + f[a[i]]); else dp[i] = max(dp[i], cf[a[i]]); } printf("%d ", n - dp[1]); return 0; }