SMU Spring 2023 Trial Contest Round 1
A. Prepend and Append
用ans记录n的值,然后双指针从前后判断是否一个为0一个为1,是的话则ans-2,否则退出循环即可.
#include<bits/stdc++.h> using namespace std; int t,n; char a[2010]; int main() { ios::sync_with_stdio(false); cin.tie(0); cin >> t; while(t--) { cin >> n; cin >> a; int ans = n; for(int i=0,j=n-1;i<=n/2,j>=n/2;i++,j--) { if(a[i] == '0' && a[j] == '1' || a[i] == '1' && a[j] == '0') ans -= 2; else break; } cout << ans << '\n'; } return 0; }
B. Distinct Split
数组s1记录子串a中出现的字符个数,数组s2记录子串b中出现的字符个数,然后将两个子串中出现的字符次数记录到数组s1中,遍历字符串,每次将si对应字符的数量从s1中减去1,然后数组s2中加1,即用i将字符串分成两个子串,然后枚举1-26个字母,取每次f(a) + f(b) 的最大值即可.
#include<bits/stdc++.h> #define int long long using namespace std; const int N = 2e5+10; int t,n; string a; int s1[N],s2[N]; signed main() { ios::sync_with_stdio(false); cin.tie(0); cin >> t; while(t--) { int res = 0; memset(s1,0,sizeof(s1)); memset(s2,0,sizeof(s2)); cin >> n; cin >> a; for(int i = 0; i < n; i ++) s1[a[i] - 'a' ] ++ ; for(int i = 0; i < n; i++) { s1[a[i] - 'a'] --; s2[a[i] - 'a'] ++; int ss1,ss2; ss1 = ss2 = 0; for(int j = 0; j < 26; j++) { if(s1[j]) ss1 ++; if(s2[j]) ss2 ++; } res = max(res, ss1 + ss2); } cout << res << '\n'; } return 0; }
C. Negatives and Positives
题意是给定一个长度为n的数组a,每次可以将相邻的两个数取相反数,即a[i] = - a[i]; a[i+1] = - a[i+1];
问数组和的最大值,共有t次询问.
即,每次我们可以找两个相邻的数,判断这两个数的和是否为负数,是的话就取相反数.
#include<bits/stdc++.h> #define int long long using namespace std; const int N = 2e5+10; int n,t,a[N],ans; signed main() { ios::sync_with_stdio(false); cin.tie(0); cin >> t; while(t--) { ans = 0; cin >> n; for(int i=1;i<=n;i++) { cin >> a[i]; ans += a[i]; } sort(a+1,a+n+1); for(int i=1;i<n;i+=2) { if(a[i] + a[i+1] < 0) { ans -= (a[i] * 2);//因为之前ans+的时候是加的负数,所以这里取反加回来要乘2 ans -= (a[i+1] * 2); } } cout << ans << "\n"; } return 0; }
D. Range Update Point Query
对于1e9以内的数,数位和最大是9*9=81,最多变成三次数位和就会变成一位数,所以我们记录每个数进行操作1的次数,然后对于查询的数进行对应次数的修改即可,所以变成了区间修改+单点查询问题
#include<bits/stdc++.h> #define int long long #define INF 0x3f3f3f3f using namespace std; const int N = 2e5+10; int n,q,m = 1e5,a[N],sum[N],ans; int lowbit(int x) { return x & (-x); } void updata(int x,int y) { for(int i = x; i <= n; i += lowbit(i)) sum[i] += y; } int shu(int x) { int res = 0; while(x) { res += x % 10; x /= 10; } return res; } int qianzui(int x) { int res = 0; for(int i = x;i ;i -= lowbit(i)) res += sum[i]; return res; } signed main() { ios::sync_with_stdio(false); cin.tie(0); int t; cin >> t; while(t--) { cin >> n >> q; for(int i = 1;i <= n;i ++) { cin >> a[i]; sum[i] = 0; } for(int i = 1;i <= q;i ++) { int m; cin >> m; if(m == 2) { int l; cin >> l; int ans = a[l]; if(a[l] >= 10) { for(int i = 1;i <= qianzui(l);i ++) { ans = shu(ans); if(ans < 10) break; } } cout << ans << '\n'; } else { int l ,r; cin >> l >> r; updata(l,1); updata(r + 1, -1); } } } return 0; }
E. Dima and Guards
题意:
有一个正整数n和 4 组正整数 a,b,c,d求是否有一组数据满足x + y = n , 其中x >= min(a, b), y >= min(c, d), .如果存在这样的x和y,则输出这一组的x 和 y 即(n - x),否则输出-1;
对于每一个 x 和 y,都保证要 min(a,b) <= x ; min(c,d) <= y, min(a,b) <= x; min(c,d) <= y。所以我们可以对 x 和 y 都取最小值,判断是否成立,如果成立,那么就输出当前的序号、最小的 x 和 n−x,最后结束程序即可。不然就输出-1,应为这已经是最小值,两数相加的和只会更大,而不会变小。
#include<bits/stdc++.h> #define int long long using namespace std; int n,a,b,c,d; signed main() { ios::sync_with_stdio(false); cin.tie(0); cin >> n; for(int i=1;i<=4;i++) { cin >> a >> b >> c >> d; int x = min(a, b), y = min(c, d); if(x + y <= n) { cout << i << ' ' << x << ' ' << n - x << '\n'; return 0; } } cout << -1 << '\n'; return 0; }
F. Dima and To-do List
题意:
emm可以理解为有n个任务,每个任务需要花费ai精力,你需要做n/k个任务, 然后你只能做间隔k个的任务,问消耗最少精力时,第一个任务的位置.
从前k个任务开始循环, 记录每次做完n/k个任务的总精力, 如果找到最小的消耗总精力就记录 i ;
值得注意的是minn比较时要开得够大不然过不了全部数据()
#include<bits/stdc++.h> #define int long long #define INF 0x3f3f3f3f using namespace std; const int N = 1e5+10; int n,k,a[N]; signed main() { ios::sync_with_stdio(false); cin.tie(0); scanf("%lld%lld",&n,&k); for(int i=1;i<=n;i++) scanf("%lld",a+i); int ans , minn = INF , sum = 0; for(int i=1;i<=k;i++) { sum = 0; for(int j=i;j<=n;j+=k) sum += a[j]; if(sum < minn) { minn = sum ; ans = i; } } printf("%lld\n",ans); return 0; }
G. Dima and Salad
这是一道背包题, 题中给的公式可转化成 a[i] - k * b[i] = 0; 即将a[i] 当作是价值, a[i] - k * b[i] 当作是重量, 0为容量, 因为a[i] - k * b[i] 有正有负, 所以可以用两个dp数组来记录, p[i] 表示质量和为i时最大值,q[i]表示质量和为 -i 时的最大值. 最后取p[i] + q[i] 中最大值即可.
#include<bits/stdc++.h> #define int long long #define INF 0x3f3f3f3f using namespace std; const int N = 1e5+10; int n,k,m = 1e5,a[N],b[N],c[N],ans; int p[N], q[N]; signed main() { ios::sync_with_stdio(false); cin.tie(0); cin >> n >> k; for(int i=1;i<=m;i++) p[i] = q[i] = -1e9; for(int i=1;i<=n;i++) cin >> a[i]; for(int i=1;i<=n;i++) { cin >> b[i]; c[i] = a[i] - k * b[i]; } for(int i=1;i<=n;i++) { if(c[i]>=0) { for(int j = m; j>= c[i]; j--) p[j] = max(p[j], p[j - c[i]] + a[i]); } else { for(int j = m; j>= -c[i]; j--) q[j] = max(q[j], q[j + c[i]] + a[i]); } } for(int i=0;i<=m;i++) ans = max(ans, p[i] + q[i]); if(!ans) puts("-1"); else cout << ans << '\n'; return 0; }
H. Dima and Trap Graph
题意:
现有一个n个点m条边的无向图, 每条边有一个区间[l,r], 求从1到n路径组成的边中, 使得该边的区间范围最大.
即从1到n的路上找到ri - li + 1最大值, 开始时先对左边 l 降序排序, 按顺序依次选中所有区间内左端点小于等于 l 的边,直到1能够到达n为止,最后把所有的ri - li + 1取max即可.
#include<bits/stdc++.h> #define int long long #define INF 0x3f3f3f3f using namespace std; const int N = 3e5+10; int n,m; struct node{ int u,v; int l,r; }edge[N]; int p[2010]; int find(int x) { return x == p[x] ? p[x] : (p[x] = find(p[x])) ; } void add(int x, int y) { int fx = find(x), fy = find(y); if(fx != fy) p[fx] = fy; } bool cmp(node x, node y) { return x.l < y.l; } signed main() { ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); cin >> n >> m; for(int i = 0;i < m;i ++) cin >> edge[i].u >> edge[i].v >> edge[i].l >> edge[i].r; sort(edge, edge + m, cmp); int ans = 0; for(int i = 0; i < m; i++) { for(int k = 1; k <= n;k ++) p[k] = k; for(int j = 0;j < m; j++) { if(edge[j].l > edge[i].r || edge[j].r <edge[i].r) continue; add(edge[j].u, edge[j].v); if(find(1) == find(n)) { ans = max(ans, edge[i].r - edge[j].l + 1); break; } } } if(!ans) puts("Nice work, Dima!"); else cout << ans << '\n'; return 0; }