题目链接: “Dashboard - The 2021 Shanghai Collegiate Programming Contest - Codeforces”
前言:剩余题目待补充
A - 小A的点面论
算法:几何知识
题解:答案为两个向量的叉积,(三维空间中,两个向量的叉积就是这两个向量确定平面的法向量) 可直接利用公式,也可以暴力枚举计算。
代码一:时间复杂度O(1)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #define endl "\n" #define IOS ios::sync_with_stdio(0);cin.tie(0); const int N = 2e3 + 100; struct node { int x, y, z; }s[N]; int main() { IOS; cin>>s[1].x>>s[1].y>>s[1].z>>s[2].x>>s[2].y>>s[2].z; cout<<s[1].y*s[2].z-s[1].z*s[2].y<<" "; cout<<s[1].z*s[2].x-s[1].x*s[2].z<<" "; cout<<s[1].x*s[2].y-s[1].y*s[2].x; return 0; }
|
代码二:时间复杂度O($n^3$)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #define endl "\n" #define IOS ios::sync_with_stdio(0);cin.tie(0); const int N = 2e3 + 100; struct node { int x, y, z; }s[N]; int main() { IOS; cin>>s[1].x>>s[1].y>>s[1].z>>s[2].x>>s[2].y>>s[2].z; for(int i = -200; i <= 200; i++) { for(int j = -200; j <= 200; j++) { for(int k = -200; k <= 200; k++) { if(i*s[1].x + j*s[1].y + k*s[1].z == 0 && i*s[2].x + j*s[2].y + k*s[2].z == 0) { cout<<i<<" "<<j<<" "<<k; return 0; } } } } return 0; }
|
B - 小A的卡牌游戏
算法:dp
题解:如果题目简化为两组数a和b,则可按照b-a 降序排序,然后贪心地先选择b再选择a。基于此贪心思想,对本题使用dp,dp[i][j]表示在前i次选择中,有j次选择第三组卡牌c,剩余i-j次按照前面贪心思想选择剩余两组卡牌。每次状态转移时,比较前后两种状态(本次是否选择该组卡牌),取max即可。注意dp数组需初始化,最大值为-1e9,可更小。
代码:时间复杂度O($n^2$)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| typedef long long ll; #define endl "\n" #define IOS ios::sync_with_stdio(0);cin.tie(0); const int N = 5e3 + 100; int n, A, B, C; struct node { int a, b, c; bool operator < (const node &y) const { if(b-a == y.b-y.a) return c > y.c; return b-a > y.b-y.a; } }v[15010]; ll dp[N][N]; int main() { IOS; cin>>n>>A>>B>>C; for(int i = 1; i <= n; i++) cin>>v[i].a>>v[i].b>>v[i].c; sort(v+1, v+n+1); for(int i = 0; i <= n; i++) for(int j = i+1; j <= C; j++) dp[i][j] = -1e9; for(int i = 1; i <= n; i++) { for(int j = 0; j <= min(C, i); j++) { if(j) dp[i][j] = max(dp[i][j], dp[i-1][j-1] + v[i].c); if(i-j <= B) dp[i][j] = max(dp[i][j], dp[i-1][j] + v[i].b); else dp[i][j] = max(dp[i][j], dp[i-1][j] + v[i].a); } } cout<<dp[n][C]; return 0; }
|
C - 小A的期末考试
算法:模拟
题解:简单模拟,注意输出的格式。
代码:时间复杂度O(n)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| #define endl "\n" #define IOS ios::sync_with_stdio(0);cin.tie(0); const double eps = 1e-6; const int N = 2e5 + 100; int n, m, sum; double mid; struct node { int a, b; bool operator < (const node &y) const { return a < y.a; } }s[N]; int main() { IOS; cin>>n>>m; for(int i = 1; i <= n; i++) { cin>>s[i].a>>s[i].b; sum += s[i].b; } mid = 1.0*sum/n; for(int i = 1; i <= n; i++) { if(s[i].a == m) { if(s[i].b < 60) s[i].b = 60; } else { if(1.0*s[i].b >= mid + eps) s[i].b -= 2; if(s[i].b < 0) s[i].b = 0; } } sort(s+1, s+n+1); for(int i = 1; i <= n-1; i++) cout<<s[i].b<<" "; cout<<s[n].b; return 0; }
|
D - Zztrans的班级合照
算法:dp
题解:dp[i][j]表示两排共选择了i个人,第一排比第二排多j个人。使用数组cnt存储相同身高的人数,将身高升序排序,去重。第一层循环表示选择身高为a[i]的学生,第二层循环表示目前第一排比第二排多j个学生,第三层循环表示在身高为a[i]的学生中选择k个放在第二排,剩余cnt[a[i]]-k个放在第一排。(这k个学生放第一排或者放第二排都可,只是状态转移方程有一些不同)
假设之前第一排有x+j个学生,第二排有x个学生,则放置k个学生之后,第一排学生总数变为x+j+cnt[a[i]]-k,第二排变为x+k,此时第一排和第二排的学生数量差值为now = j+cnt[a[i]]-2*k,当差值大于0的时候才是有效转移。所以现在的状态dp[i][now]可以从上次的的状态dp[i-1][j]转移,由于选择的是身高为a[i]的一类学生,学生们身高一样但代表着的还是不同的人,所以需要乘以该身高学生个数的阶乘。(阶乘在开始预处理并存储在数组里面)
代码:时间复杂度O($n^2$)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| typedef long long ll; #define endl "\n" #define IOS ios::sync_with_stdio(0);cin.tie(0); const int mod = 998244353; const int N = 5e3 + 100; int n; ll a[N], cnt[N], dp[N][N], fac[N]; void init_fac() { fac[1] = 1; for(int i = 2; i <= N; i++) (fac[i] = fac[i-1]*i)%=mod; } int main() { IOS; init_fac(); cin>>n; for(int i = 1; i <= n; i++) { cin>>a[i]; cnt[a[i]]++; } sort(a+1, a+n+1); int nn = unique(a+1, a+n+1)-(a+1); dp[0][0] = 1; for(int i = 1; i <= nn; i++) { for(int j = 0; j <= n; j++) { for(int k = 0; k <= cnt[a[i]]; k++) { int now = j+cnt[a[i]]-2*k; if(now < 0) break; (dp[i][now] += dp[i-1][j]*fac[cnt[a[i]]])%=mod; } } } cout<<dp[nn][0]; return 0; }
|
E - Zztrans的庄园
算法:概率与期望
题解:基本期望知识。期望定义式:
代码:时间复杂度:O(n)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #define IOS ios::sync_with_stdio(0);cin.tie(0); int n, k; double sum; int main() { scanf("%d %d", &n, &k); for(int i = 1; i <= n; i++) { char s; double x; cin>>s>>x; if(s == 'A') x *= 57; if(s == 'B') x *= 31; if(s == 'C') x *= 1; if(s == 'D') x *= -7; if(s == 'S') x *= 9977; sum += x; } printf("%.4f", sum*k); return 0; }
|
G - 鸡哥的雕像
算法:逆元
题解:每个位置的答案就是所有的数的乘积除以该位置上的数字并对998244353取模,使用逆元计算sum/a[i],即sum*ksm(a[i], mod-2)(ksm为快速幂函数)。但由于998244353 < 1e9。所以分三种情况:给出的数中有两个及以上的998244353,这样的话所有的位置最终结果都为0;给出的数中有一位是998244353,则只有这一位上的结果为其它为上数的乘积,其他位全为0;没有出现998244353,则按照正常公式计算。
代码:时间复杂度O(n)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| typedef long long ll; #define endl "\n" #define IOS ios::sync_with_stdio(0);cin.tie(0); const int mod = 998244353; const int N = 1e5 + 100; ll ksm(ll a, ll b) { ll res = 1; while(b) { if(b&1) (res *= a)%=mod; (a *= a)%=mod; b>>=1; } return res; } ll n, cnt; ll a[N]; int vis[N]; int main() { IOS; ll sum = 1; cin>>n; for(int i = 1; i <= n; i++) { cin>>a[i]; if(a[i] == mod) vis[i] = 1, cnt++; else (sum *= a[i])%=mod; } if(cnt >= 2) { for(int i = 1; i <= n-1; i++) cout<<0<<" "; cout<<0; } else if(cnt == 1) { for(int i = 1; i <= n-1; i++) { if(vis[i]) cout<<sum<<" "; else cout<<0<<" "; } if(vis[n]) cout<<sum; else cout<<0; } else { for(int i = 1; i <= n-1 ;i++) cout<<sum*ksm(a[i], mod-2)%mod<<" "; cout<<sum*ksm(a[n], mod-2)%mod; } return 0; }
|
J - Alice and Bob 1
算法:思维
题解:若数据全为正值:在轮流取卡片的过程中,Alice拿当前最大的,Bob取第二大的即可保证Alice取得的为最大值。但由于题目最后的结果为绝对值且所给数据中有负值,所以在数据按递减排序后,会有Alice从前往后取,先取正值再取负值综合为正值,和从后往前取,先取负值再取正值总和为负值,这两种结果的绝对值都有可能是最大值。
例如第一组数据: 5 3 2 1 -1 -2 这种情况下Alice要从前往后取。第二组数据:1 2 -3 -5 -7 -8这种情况下Alice要从后往前取。
代码:时间复杂度O(n)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| typedef long long ll; #define IOS ios::sync_with_stdio(0);cin.tie(0); const int N = 5e3 + 100; int n; ll sum1, sum2, sum; int a[N]; bool cmp(int x, int y) { return x > y; } int main() { IOS; cin>>n; for(int i = 1; i <= n; i++) cin>>a[i], sum += a[i]; sort(a+1, a+n+1, cmp); for(int i = 1; i <= n; i += 2) sum1 += a[i]; for(int i = n; i >= 1; i -= 2) sum2 += a[i]; if(abs(sum1) > abs(sum2)) cout<<(ll)abs(sum1) - (ll)abs(sum-sum1); else cout<<(ll)abs(sum2) - (ll)abs(sum-sum2); return 0; }
|
/Image-1.png)