A
枚举一下
#include <bits/stdc++.h> using namespace std; int n; string End; string s[55]; int t[55]; int main() { cin >> n; for(int i = 1; i <= n; ++i) { cin >> s[i] >> t[i]; } cin >> End; int ans = 0; for(int i = n; i; --i) { if(s[i] == End) { break; } ans += t[i]; } cout << ans; return 0; }
B
期望
按每个空隙计算 对于第$i$个空隙 对于第$j$个跨过这段的概率是$frac{1}{i-j+1}$ 因为跨过当且仅当$[j+1,i]$之间都不先于$j$合并 求一个逆元前缀和即可
还可以这样做 $dp[i]$表示第$i$个空隙期望经过人数
那么$dp[1] = 1$
$dp[n] = dp[n - 1] + frac{1}{n}$
因为考虑任意一个人动 只有最右边的人动会造成贡献
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5 + 5, P = 1e9 + 7; int n; int x[maxn], inv[maxn]; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cin >> n; for(int i = 1; i <= n; ++i) { cin >> x[i]; } inv[1] = 1; for(int i = 2; i <= n; ++i) { inv[i] = 1LL * (P - P / i) * inv[P % i] % P; } for(int i = 2; i <= n; ++i) { inv[i] = (inv[i] + inv[i - 1]) % P; } int ans = 0; for(int i = 1; i < n; ++i) { ans = (ans + 1LL * inv[i] % P * (x[i + 1] - x[i]) % P) % P; } for(int i = 1; i < n; ++i) { ans = 1LL * ans * i % P; } cout << ans << ' '; return 0; }
C
乘上组合数后相当于计算所有情况下的开心值
问题可以转化成有一个$K*N$的表格 每一行涂$a[i]$个红格子 再将每一列的红格子中选一个涂蓝的方案数
$dp[i][j]$表示前i行涂了j列
转移$dp[i][j] * C(n - j, l) * C(n - l, a[i + 1] - l) -> dp[i + 1][j + l]$
表示下一行先钦定l个蓝色格子 且这些蓝色格子所在列没有蓝色格子 剩下随便选涂红色
#include <bits/stdc++.h> using namespace std; const int maxn = 1005, P = 1e9 + 7; int n, k; int a[maxn]; int c[maxn][maxn], dp[22][maxn]; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cin >> n >> k; for(int i = 1; i <= k; ++i) { cin >> a[i]; } c[0][0] = 1; for(int i = 1; i <= n; ++i) { c[i][0] = 1; for(int j = 1; j <= i; ++j) { c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % P; } } dp[0][0] = 1; for(int i = 0; i < k; ++i) { for(int j = 0; j <= n; ++j) { for(int l = 0; l + j <= n && l <= a[i + 1]; ++l) { dp[i + 1][j + l] = (dp[i + 1][j + l] + 1LL * dp[i][j] * c[n - j][l] % P * c[n - l][a[i + 1] - l] % P) % P; } } } cout << dp[k][n] << ' '; return 0; }
D
问题转化成求补图字典序最小的哈密尔顿回路
题解里写这样貌似的路径很好找 于是先贪心填 如果当前存在入度等于剩下除自身外点数的点 那么填上 否则不合法 否则就填字典序最小的 用两个set维护一下
但是会不合法 于是最后几个点爆搜一下 具体证明不会
这道题告诉我们碰见这种npc问题要考虑乱搞贪心以及爆搜 说不定就过了
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5 + 5; int n; int a[maxn], d[maxn], used[maxn], ans[maxn]; set<int> s; set<pair<int, int> > e; vector<int> b; void dfs(int p, int ban) { if(p == n + 1) { for(int i = 1; i <= n; ++i) { cout << ans[i] << ' '; } cout << ' '; exit(0); } for(int i = 0; i < b.size(); ++i) { if(!used[b[i]] && b[i] != ban) { used[b[i]] = 1; ans[p] = b[i]; dfs(p + 1, a[b[i]]); used[b[i]] = 0; } } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cin >> n; for(int i = 1; i <= n; ++i) { cin >> a[i]; ++d[a[i]]; } for(int i = 1; i <= n; ++i) { s.emplace(i); } for(int i = 1; i <= n; ++i) { e.emplace(-d[i], i); } int ban = 0; for(int i = 1; i <= n - 5; ++i) { int u; if(-e.begin() -> first == n - i) { u = e.begin() -> second; } else if(*s.begin() != ban) { u = *s.begin(); } else { u = *next(s.begin()); } s.erase(u); e.erase({-d[u], u}); used[u] = 1; if(!used[a[u]]) { e.erase({-d[a[u]], a[u]}); --d[a[u]]; e.emplace(-d[a[u]], a[u]); } ban = a[u]; ans[i] = u; } b.assign(s.begin(), s.end()); dfs(max(1, n - 4), ban); cout << -1 << ' '; return 0; }
E
太难了不会