北京信息科技大学第十二届程序设计竞赛暨ACM选拔赛(同步赛)
爱丽丝的人偶(一)
https://ac.nowcoder.com/acm/contest/8755/A
个人题解(做完题赶快写题解,趁热打铁,希望对你有帮助)
A题链接:https://ac.nowcoder.com/acm/contest/8755/A
题目描述:找到任意一组满足题目中描述的条件的数组就好了
思路:n,1,n-1,2,n-2,3.... 这样就是一组解
#include <iostream> using namespace std; const int N = 110; int t,n,m; char g[N][N]; int main() { int n; cin >> n; int l = 1,r = n; while(l < r) { cout << r << ' ' << l << ' '; r --; l ++; } if(n % 2 != 0) cout << l << endl; return 0; }
B题链接:https://ac.nowcoder.com/acm/contest/8755/B
题目描述:求
思路:先算出该怎么计算就怎么算,比如 ,就先算出,然后再算出,那么答案不就是吗,因为这个地方算出的结果可能很大,所以要在算的过程中不断地取模,而且这个让取模的数是个大素数,所以可以通过乘法逆元来求出最终答案, ,这个可以通过快速幂迅速求出来
#include <iostream> #include <set> using namespace std; typedef long long LL; const int mod = 1e9 + 7; LL n,k; set<LL>st; LL qmi(LL a,LL b) { LL res = 1; while(b){ if(b & 1) res = res * a % mod; a = a * a % mod; b = b >> 1; } return res; } int main() { cin >> n >> k; for(int i = 0;i < n;i ++) { LL x; cin >> x; st.insert(x); } LL m = st.size(); LL s1 = 1,s2 = 1; for(int i = m;i > m - k;i --) { s1 = s1 * i % mod; } for(int i = 1;i <= k;i ++) { s2 = s2 * i % mod; } LL s3 = s1 * qmi(s2,mod - 2) % mod; cout << s3 << endl; return 0; }
C题链接:https://ac.nowcoder.com/acm/contest/8755/C
题目描述:简简单单的思维题
思路:输出A的情况就是1,2或者2,1
#include <iostream> using namespace std; const int N = 110; int t,n,m; char g[N][N]; int main() { int a,b; cin >> a >> b; if(a == 1 && b == 2 || a == 2 && b == 1){ cout << 'A' << endl; }else{ cout << 'B' << endl; } return 0; }
D题链接:https://ac.nowcoder.com/acm/contest/8755/D
题目描述:如果能在有限次操作将字符串变成匹配的,请输出最少的操作次数。否则输出-1
思路:先把那些不能匹配的括号找出来,找出来的括号肯定是类似于这个样子的))))((((,如果这样的括号数量为奇数,那么肯定不能通过一定的操作次数让他们都找到自己的匹配方,所以此时 l + r肯定是偶数,如果l是偶数 那么答案就是 l + r >> 1,比如)))),l = 4,r = 0,操作两次就可以达到我们的目的,如果l是奇数,那么r也肯定是奇数,所以答案就是(l + 1 >> 1 ) + (r + 1 >> 1),这里画个图就好了
#include <iostream> #include <stack> using namespace std; typedef long long LL; int main() { int n; string str; cin >> n; cin >> str; stack<char>stk; int l = 0,r = 0; for(auto &x : str){ if(stk.size()){ if(x == ')'){ if(stk.top() == '('){ stk.pop(); }else{ stk.push(x); } } else{ stk.push(x); } }else{ stk.push(x); } } while(stk.size()) { if(stk.top() == ')'){ l ++; }else{ r ++; } stk.pop(); } if((l + r) % 2 != 0){ cout << -1 << endl; } else{ if(l % 2 == 0){ cout << (l + r >> 1 )<< endl; }else{ int ans = l + 1 >> 1; ans += r + 1 >> 1; cout << ans << endl; } } return 0; }
E题链接:https://ac.nowcoder.com/acm/contest/8755/E
题目描述:输入一个01字符串,有多少个字串可以操作k次,这里的k次不懂得话,看样例说明
思路:先统计出每段1和0的个数,如果k等于1的话,那么对于每一段就是1 + 2 + 3 + ....,求和公式,否则就是i就是从第k项开始,看第i项与第i-k项乘起来,具体看代码
#include <iostream> #include <queue> using namespace std; typedef long long LL; LL n,k; int main() { string str; cin >> n >> k; cin >> str; vector<int>v; for(int i = 0;i < n;i ++){ int cnt = 0; int j = i; for(j = i;j < n;j ++){ if(str[j] == str[i]){ cnt ++; }else{ break; } } i = j - 1; v.push_back(cnt); } LL res = 0; if(k == 1){ for(auto &x : v){ res += 1ll*(x + 1)* x / 2; } }else{ k --; for(int i = k;i < v.size();i ++) { res += 1ll*v[i] * v[i - k] ; } } cout << res << endl; return 0; }
F题链接:https://ac.nowcoder.com/acm/contest/8755/F
题目描述:小A每次可以任取 连续的 未被吞噬过的 三部分,将其中的黑暗全部吞噬,并获得中间部分的饱食度,小A想知道,自己能获得的饱食度最大值是多少
思路:dp[i]代表以第i项为结尾的最大值是多少,从第四项开始计算,小A可以吃当前这个黑暗数量dp[i - 3] + a[i],也可以不吃dp[i - 1]
dp[i] = max(dp[i - 3] + a[i],dp[i - 1]);
#include <iostream> #include <queue> using namespace std; typedef long long LL; const int N = 1e5 + 10; LL dp[N]; LL a[N]; int main() { int n; cin >> n; for(int i = 1;i <= n;i ++) cin >> a[i]; dp[2] = a[2]; dp[3] = max(a[3],a[2]); LL res = a[3]; for(int i = 4;i < n;i ++) { dp[i] = max(dp[i - 3] + a[i],dp[i - 1]); res = max(res,dp[i]); } if(n == 3){ res = dp[2]; } cout << res << endl; return 0; }
G题链接:https://ac.nowcoder.com/acm/contest/8755/G
题目描述:略
思路:找到所有数的最大公约数,然后判断一下给的数能否整除最大公约数 (证明略),最大公约数我一般都是调用库函数,你也可以自己去写.
#include <iostream> #include <algorithm> using namespace std; typedef long long LL; const int N = 1e5 + 10; LL n,k; LL t; LL a[N]; int main() { cin >> n; for(int i = 1;i <= n;i ++){ cin >> a[i]; } t = a[1]; for(int i = 2;i <= n;i ++){ t = __gcd(a[i],t); } cin >> k; while(k --){ LL x; cin >> x; if(x % t == 0){ cout << "Yes" << endl; }else{ cout << "No" << endl; } } return 0; }
H题链接:https://ac.nowcoder.com/acm/contest/8755/H
题目描述:找到一个最长链
思路:通过dp来求树的直径的一种变形,如果两个点可以到达也就是颜色不一样,那么 res = max(res,dp[u] + dp[j] + 1);dp[u] = max(dp[u],dp[j] + 1); 否则res = max(res,dp[u]);之前写过一个两种算法求树的直径的博客(写的不太好),有兴趣的同学可以看看https://blog.csdn.net/weixin_44235647/article/details/106661379
#include <cstring> #include <iostream> #include <algorithm> using namespace std; const int mod = 1e9 + 7; typedef long long LL; const int N = 1e6 + 10; int n; int w[N]; int h[N],e[N],ne[N],idx; void add(int a,int b) { e[idx] = b,ne[idx] = h[a],h[a] = idx ++; } int res = 0; int dp[N]; void dfs(int u,int fa) { for(int i = h[u];~i;i = ne[i]) { int j = e[i]; if(j == fa) continue; dfs(j,u); if(w[u] == w[j]){ res = max(res,dp[u]); }else{ res = max(res,dp[u] + dp[j] + 1); dp[u] = max(dp[u],dp[j] + 1); } } } int main() { memset(h,-1,sizeof h); cin >> n; for(int i = 1;i <= n;i ++){ char c; cin >> c; if(c == 'R'){ w[i] = 1; }else if(c == 'G'){ w[i] = 2; } } for(int i = 0;i < n - 1;i ++) { int a,b; cin >> a >> b; add(a,b); add(b,a); } dfs(1,0); cout << res << endl; return 0; }
I题链接:https://ac.nowcoder.com/acm/contest/8755/I
题目描述:这个考察应该是逆元,而B题考察的应该是卢卡斯定理
思路: ,然后注意一下精度,不懂得话,就全开long long
#include <iostream> #include <algorithm> using namespace std; const int mod = 1e9 + 7; typedef long long LL; const int N = 1e6 + 10; LL n,k,m; LL t; LL a[N]; LL qmi(LL a,LL b) { LL res = 1; while(b){ if(b & 1) res = res * a % mod; a = a * a % mod; b = b >> 1; } return res; } int main() { scanf("%lld%lld%lld",&n,&m,&k); LL s1 = 0,s2 = 0,s3 = 0; for(int i = 1;i <= n;i ++){ int x; scanf("%d",&x); s1 += x; } for(int i = 1;i <= m;i ++){ int x; scanf("%d",&x); s2 += x; } for(int i = 1;i <= k;i ++){ int x; scanf("%d",&x); s3 += x; } s1 = s1 % mod; s2 = s2 % mod; s3 = s3 % mod; LL sum = s1 * s2 % mod * s3 % mod; LL sum1 = n * m % mod * k % mod; printf("%lld",sum*qmi(sum1,mod - 2) % mod); return 0; }
J题链接:https://ac.nowcoder.com/acm/contest/8755/J
题目描述:见题目说明
思路:先求出a,b的最近公共祖先t,然后统计根节点0到a的路径上所有不同的字母分别对应的总数,0到b的路径上所有不同的字母分别对应的总数,然后减去0到t的路径上所有不同的字母分别对应的总数,再把节点t对应的字母加上相应位置,如果字母出现的总数为偶数次那么加上答案,如果为奇数次,就加上奇数次-1,最终答案再加1; 倍增法求LCA
#include <cstring> #include <iostream> #include <algorithm> using namespace std; const int mod = 1e9 + 7; typedef long long LL; const int N = 1e6 + 10; int n,q; int w[N]; int h[N],e[N],ne[N],idx; void add(int a,int b) { e[idx] = b,ne[idx] = h[a],h[a] = idx ++; } int dist[N]; int dp[N][30]; int f[N][20]; void dfs(int u,int fa) { for(int i = h[u];~i;i = ne[i]) { int j = e[i]; if(j == fa) continue; dist[j] = dist[u] + 1; for(int i = 1;i <= 26;i ++) dp[j][i] = dp[u][i]; int c = w[j]; dp[j][c] ++; f[j][0] = u; for(int i = 1;i <= 16;i ++) { int k = f[j][i - 1]; f[j][i] = f[k][i - 1]; } dfs(j,u); } } int lca(int a,int b) { if(dist[a] < dist[b]){ swap(a,b); } for(int i = 16;i >= 0;i --) { if(dist[f[a][i]] >= dist[b]){ a = f[a][i]; } } if(a == b){ return a; } for(int i = 16;i >= 0;i --) { if(f[a][i] != f[b][i]){ a = f[a][i]; b = f[b][i]; } } return f[a][0]; } int main() { memset(h,-1,sizeof h); cin >> n; for(int i = 0;i < n - 1;i ++) { int a,b; cin >> a >> b; add(a,b); add(b,a); } for(int i = 1;i <= n;i ++) { char c; cin >> c; w[i] = c - 'a' + 1; } dist[1] = 1; dfs(1,0); cin >> q; while(q --) { int a,b; cin >> a >> b; int t = lca(a,b); int arr[28] = {0}; for(int i = 1;i <= 26;i ++) arr[i] += dp[a][i]; for(int i = 1;i <= 26;i ++) arr[i] += dp[b][i]; for(int i = 1;i <= 26;i ++) arr[i] -= dp[t][i] * 2; arr[w[t]] ++; int cnt = 0; bool flag = false; for(int i = 1;i <= 26;i ++) { if(arr[i] % 2 == 0){ cnt += arr[i]; }else if(arr[i]){ cnt += arr[i] - 1; flag = true; } } cout << cnt + flag << endl; } return 0; }
k题链接:https://ac.nowcoder.com/acm/contest/8755/k
题目描述:略
思路:m 除以二 上取整 判断与k的大小关系
#include <iostream> using namespace std; const int N = 110; int t,n,m; char g[N][N]; int main() { int n,m,k; cin >> n >> m >> k; m = m + 1 >> 1; if(m <= k){ cout << "Yes" << endl; }else{ cout << "No" << endl; } return 0; }