Codeforces Round 957 (Div. 3)
2024-07-12 —yimg
A
簽到題
程式碼:
#include<bits/stdc++.h>
using namespace std;
void work()
{
vector<int> a(3);
for(int i = 0; i < 3; ++i) cin >> a[i];
int t = 5;
while(t--){
sort(a.begin(), a.end());
a[0]++;
}
cout << a[0]*a[1]*a[2] << '\n';
}
int main()
{
int t;
cin >> t;
while(t--)
work();
}
B
簽到題
程式碼:
#include<bits/stdc++.h>
using namespace std;
void work()
{
int n, k;
cin >> n >> k;
vector<int> a(k);
for(auto &i: a) cin >> i;
sort(a.begin(), a.end());
int ans = 0;
for(int i = 0; i < k - 1; ++i){
ans += a[i] - 1;
}
cout << ans + n - a[k - 1] << '\n';
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--)
work();
}
C
簽到題
程式碼:
#include<bits/stdc++.h>
using namespace std;
void work()
{
int n, m, k;
cin >> n >> m >> k;
vector<int> a(n + 1);
int j = 1;
for(int i = n; i > m; --i, ++j){
a[j] = i;
}
for(int i = 1; i <= m; ++i, ++j){
a[j] = i;
}
for(int i = 1; i <= n; ++i)
cout << a[i] << " ";
cout << "\n";
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--)
work();
}
D
題意:
ErnKor過河,這條河有n個格子,L代表平臺可以跳往[L,L + m]的任意位置,W代表水,每經過一格水要消耗一個體力,最開始有k個體力,也就是最多經過k格水,C代表鱷魚,不能待在C所在的格子。給定n,m,k 和一個代表河流的字串,問能否到達對岸
\(1\le m\le10,0\le k \le 2*10^{5},1\le n \le2*10^{5}\)
思路:
-
比較容易想到的是用貪心來做,如果在能跳往下一個平臺,跳往下一個平臺一定是最優操作,如果不能,跳滿m格是最優操作,其中會判NO的情況是在水中遇到’C’或者體力不夠用,複雜組O(n + k)
-
dp來解決問題也很淺顯,我們記錄到當前格子要消耗的最小體力,我們當前的格子可能是由前面的格子跳過來的,或者由上一個水游過來的。 鱷魚初始為極大值後不用管,因為我們不能透過‘C’轉移。複雜度為O(nm)。
\[dp_i = \left\{ \begin{array}{rcl} dp_j & & 0\le i - j \le m, s_j='L'\\ dp_{i-1} + 1 & &s_{i-1}='W'\\ \end{array} \right. \]
程式碼:
貪心
#include<bits/stdc++.h>
using namespace std;
void work()
{
int n, m, k;
cin >> n >> m >> k;
string s;
cin >> s;
for(int l = -1, r = 0; r <= s.length(); ++r){
if(l == s.length()) break;
if(l + m < r){
l = l + m;
if(s[l] == 'C'){
cout << "NO\n";
return;
}
while(1){
k--;
++l;
if(s[l] == 'C' || k < 0){
cout << "NO\n";
return;
}
if(l == s.length() || s[l] == 'L'){
r = l;
break;
}
}
}
else if(r == s.length() || s[r] == 'L'){
if(l + m >= r){
l = r;
}
}
}
cout << "YES\n";
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--)
work();
}
DP
#include<bits/stdc++.h>
using namespace std;
void work()
{
int n, m, k;
cin >> n >> m >> k;
string s;
cin >> s;
s = "L" + s + "L";
vector<int> f(n + 5, 1e9 + 7);
f[0] = 0;
for(int i = 1; i <= n + 1; ++i){
for(int j = 1; j <= m; ++j){
if(i - j >= 0 && s[i - j] == 'L')
f[i] = min(f[i], f[i - j]);
}
if(s[i - 1] == 'W') f[i] = min(f[i], f[i - 1] + 1);
}
if(f[n + 1] <= k) cout << "YES\n";
else cout << "NO\n";
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--)
work();
}
E
題意:
給定n, 問有多少對符合要求的數對(a, b)滿足 \(n*a-b=f(n, a, b)\)
定義\(f(n,a,b)\)為將n個a順序拼在一起,刪去後b位剩下的數
\(1\le a \le10^4, 1\le b \le min(10^4,n*a)\)
思路:
a和b的範圍都很小,直接列舉a,對於此a我們限制b的範圍,\(b = a*len(n)-len(f)\) , 對每個a我們列舉\(b\in[a*len(n)-1,a*len(n)-5]\) 驗證答案。
程式碼:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
void work()
{
int n;
cin >> n;
string chn = to_string(n);
string s;
int ln = chn.length();
for(int i = 1; i <= 10; ++i) s += chn;
vector<pair<int, int>> ans;
const int N = 1e4;
for(int a = 1; a <= N; ++a){
for(int b = max(a*ln-5, 1); b <= min(a*ln-1, min(N, a*n)); ++b){
int num = 0;
for(int i = 0; i < ln*a-b; ++i) num = num * 10 + s[i] - '0';
if(n*a-b == num) ans.push_back({a,b});
}
}
cout << ans.size() << '\n';
for(auto v : ans)
cout << v.first << " " << v.second << '\n';
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--)
work();
}
F
題意:
一個段\((l,r)\) 如果不存在索引 \(i_1 < i_2 < \ldots < i_k\) ,\(l \le i_1, i_k \le r\) 使得 \(a_{i_1} \cdot a_{i_2} \ldots \cdot a_{i_k} = x\)
則稱這個段為壞段,每個元素屬於且只屬於一個段,且分段後,所有的段都是壞段,問壞段的最小數量
思路:
貪心地分段,如果一個元素可以分到上一個段就把它放到上一個段,否則分一個新段。
實現上,可以維護使當前段乘積為x的元素(也就是因子),要避免重複可以用set,複雜度為O(nlog(D(x)))
程式碼:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
void work()
{
int n, x;
cin >> n >> x;
vector<int> a(n + 1);
for(int i = 1; i <= n; ++i) cin >> a[i];
set<int> s;
vector<int> tmp;
int ans = 1;
s.insert(x);
for(int i = 1; i <= n; ++i){
if(a[i] == 1) continue;
tmp.clear();
for(auto el : s) {
if(el%a[i] == 0)
tmp.push_back(el/a[i]);
}
for(auto el : tmp) s.insert(el);
if(s.find(1) != s.end()){
ans++;
s.clear();
s.insert(x);
s.insert(x/a[i]);
}
}
cout << ans << '\n';
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--)
work();
}
G
題意:
求\(MEOW(a)\)的值,\(MEOW(a)\) 是 \(MEX(b,|b|+1)\) 的和,b為a的所有不同子集
a為長度為n的陣列{1,2,3,…, n}
\(MEX(S,k)\) 為不包含S中元素的第k大的正整數
思路:
當我們選k個數為子集時:
- 當\(2*k+1>n\) 時,MEX值固定為\(2k+1\) ,子集有 \(C_n^k\) 種
- 當\(2*k+1\le n\) 時,MEX值不固定,但是我們可以列舉MEX值,令MEX值為m,我們要算出m-1個數有多少種選法
- 選比m小的數要從\([1,m-1]\) 中選\(m-1-k\)個, 有\(C_{m-1}^{m-1-k}\) 種
- 選比m大的數要從\([m+1,n]\) 中選\(2k+1-m\) 個, 有\(C_{n-m}^{2k+1-m}\) 種
複雜度為\(O(n^2)\)
實現上,求組合數時要用到乘法逆元,預處理好即可
程式碼:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 5000;
const int mod = 1e9 + 7;
ll fac[N + 5], inv[N + 5];
void init()
{
fac[0] = fac[1] = 1;
for(int i = 1; i <= N; ++i)
fac[i] = fac[i - 1] * i % mod;
auto qpow = [&](ll base, ll p){
ll res = 1;
while(p){
if(p&1) res *= base, res %= mod;
base *= base; base %= mod;
p >>= 1;
}
return res;
};
inv[N] = qpow(fac[N], mod - 2);
for(int i = N - 1; i >= 0; --i)
inv[i] = inv[i + 1] * (i + 1) % mod;
}
ll C(int a, int b){
if(a < 0 || b < 0 || a - b < 0) return 0;
return fac[a] * inv[b] % mod * inv[a-b] % mod;
}
void work()
{
int n;
cin >> n;
ll ans = 1;
for(int k = 1; k <= n; ++k){
if(2*k+1 > n){
ans += (2*k+1) * C(n, k) % mod;
ans %= mod;
continue;
}
for(int m = k + 1; m <= 2 * k + 1; ++m){
ans += m * C(m-1, m-1-k) % mod * C(n-m, 2*k+1-m) % mod;
ans %= mod;
}
}
cout << ans << '\n';
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
init();
while(t--)
work();
}