23暑假友谊赛 No.3
A - 把你砍成两半!
在本地找规律就会发现,当\(a_1\)确定时,\(a_i\)一定是\(a_1\)的倍数。所以答案就是
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 998244353;
vector<int> fact, invFact;
int power(int x, int y) {
int ans = 1;
while (y) {
if (y & 1) ans = ans * x % mod;
y >>= 1, x = x * x % mod;
}
return ans;
}
int inv(int x) {
return power(x, mod - 2);
}
void init(int n) {
fact = vector<int>(n + 1), invFact = vector<int>(n + 1);
fact[0] = 1, invFact[0] = inv(1);
for (int i = 1; i <= n; i++)
fact[i] = fact[i - 1] * i % mod, invFact[i] = inv(fact[i]);
}
int C(int x, int y) {
assert(x >= y);
return fact[x] * invFact[x - y] % mod * invFact[y] % mod;
}
int32_t main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int n, k, res = 0;
cin >> n >> k;
if (n < k) {
cout << "0\n";
return 0;
}
init(n);
for (int i = 1; i * k <= n; i++)
res = (res + C(n / i - 1, k - 1)) % mod;
cout << res;
return 0;
}
证明可以去看看原题链接给的题解
B - 小叶,我们加油!
因为只有\(1\times1,1\times2\)两种白色瓷砖,所以每一行单独 dp 求一下就好了。
#include <bits/stdc++.h>
using namespace std;
void solve() {
int n, m, a, b, res = 0;
cin >> n >> m >> a >> b;
string g;
for (int i = 1; i <= n; i++) {
cin >> g;
vector<int> f(m + 1);
for (int j = 1; j <= m; j++) {
if (g[j - 1] == '*') f[j] = f[j - 1];
else {
f[j] = f[j - 1] + a;
if (j - 2 >= 0 && g[j - 2] == '.')
f[j] = min(f[j], f[j - 2] + b);
}
}
res += f[m];
}
cout << res << "\n";
return;
}
int32_t main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int T;
cin >> T;
while (T--)
solve();
return 0;
}
C - 工程学的作用不可小觑!
可以想到答案一定是有两个端点构成的,自然可以\(C_4^2\) 的枚举出所有可能的解,再逐个判断就好
#include <bits/stdc++.h>
using namespace std;
void solve(){
array<int,4> a;
for( auto & i : a ) cin >> i;
for( auto i : a)
for( auto j : a )
if( i != j && a[0] <= i && i <= a[1] && a[2] <= j && j <= a[3] ){
cout << i << " " << j << "\n";
return ;
}
return ;
}
int32_t main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int T;
cin >> T;
while( T -- )
solve();
return 0;
}
D - 做博士的护卫?好哦。
我们要找同时满足\(1\le a\le b \le c \le n , a^2+b^2=c^2,a^2-b=c\)
的\((a,b,c)\)的数量,首先把两个等式作差可以得到\(b^2+b=c^2-c\)则\(b(b+1)=c(c-1)\),所以\(c=b+1\)
带回原式可得\(a^2=2b+1\)因为\(b\le n\)所以\(a^2=2b+1\le 2n+1\)。这样我们\(O(\sqrt n)\)的求出所有符合条件的\(a\)即可,当然也可以用数学方法\(O(1)\)的解出答案。
另外一点要注意的是\(a^2=2b+1\),所以\(a\)一定是奇数。
#include <bits/stdc++.h>
using namespace std;
#define int long long
void solve(){
int n , res = 0;
cin >> n;
for( int i = 3 , N = 2*n-1; i * i <= N ; i += 2 )
res ++;
cout << res << "\n";
}
int32_t main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int t;
cin >> t;
while( t -- )
solve();
return 0;
}
E - 暴虐的恶人阻断正义的道路,我的主人啊,以复仇与恶意为名,引领弱小的人把。
对于同一种硬币每枚硬币要占用一个口袋,统计那种硬币占用口袋数量最多即可。
#include <bits/stdc++.h>
using namespace std;
int32_t main() {
int n;
cin >> n;
vector cnt( 101 , 0 );
for( int x ; n ; n -- )
cin >> x , cnt[x] ++;
cout << *max_element(cnt.begin(), cnt.end());
return 0;
}
F - 我才不会一个人的时候跟仿生海龙聊天!
设\(f[i][j]\)表示第\(i\)种菜,选择\(j\)的最小代价,则转移方程就是\(f[i][j]=\min f[i-1][k] + a[i][j],(k,j)\notin\{(x_{i-1},y_{i-1})\}\),这样的话实际上转换成了一个带修改的 RMQ问题,并且每次询问的区间都是完整的区间,当然我们可以使用各种数据结构来解决这个问题。
这里我们 multiset 来维护\(f[i-1][k]\)的最小值,每次枚举点后,把所有不能转移过来的点删掉,计算答案后再重新加回去,复杂度\(O(m\log n)\)。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int inf = 1e18;
int32_t main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
vector<int> n(4);
for (auto &i: n) cin >> i;
vector<vector<int>> a(4);
for (int i = 0; i < 4; i++) {
a[i] = vector<int>(n[i]);
for (auto &j: a[i]) cin >> j;
}
for (int i = 1, m; i < 4; i++) {
cin >> m;
vector<vector<int>> e(n[i] + 1);
for (int x, y; m; m--)
cin >> x >> y, x--, y--, e[y].push_back(x);
multiset<int> cnt;
for (auto j: a[i - 1])
cnt.insert(j);
for (int j = 0; j < n[i]; j++) {
for (auto k: e[j])
cnt.erase(cnt.lower_bound(a[i - 1][k]));
if (cnt.empty()) a[i][j] = inf;
else a[i][j] += *cnt.begin();
for (auto k: e[j])
cnt.insert(a[i - 1][k]);
}
}
int res = *min_element(a[3].begin(), a[3].end());
if( res >= inf ) res = -1;
cout << res;
return 0;
}
G - 不让他们过去就可以了吗?
统计与最小值不同的数字数量。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int M = (1 << 15) - 1;
void solve(){
int n;
cin >> n;
vector<int> a(n);
for( auto &i : a )
cin >> i;
sort(a.begin(), a.end());
for( auto i : a ){
if( i != a[0] ) break;
n --;
}
cout << n << "\n";
}
int32_t main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int t;
cin >> t;
while( t -- )
solve();
return 0;
}
H - 唔...这个我真的能做到吗。
其实混合只有两种情况
- \(x\)杯热水,\(x\)杯凉水,温度一定\(\frac{h + c }{ 2}\)
- \(x+1\)杯热水,\(x\)杯凉水,水温是\(T_x=\frac{(x+1)h+xc}{2x+1}\),并且\(T_x\in(\frac{h+c}{2},h],T_x>T_{x+1}\)
知道这些之后其实很好做了,如果\(t\le \frac{h+c}{2}\)答案一定是 2,否则可以二分答案。
其实对于第二种情况如果解\(T_x=t\)可以解出\(x=\frac{t-h}{h+c-2t}\),但是涉及到取证的问题,比较简单的做法是计算\(x-,x,x+1\)取最优解即可。
#include <bits/stdc++.h>
using namespace std;
#define int long long
typedef long double ld;
void solve() {
int c, h, T;
cin >> h >> c >> T;
if (c + h >= T * 2) {
cout << "2\n";
return;
}
int x = (T - h) / (h + c - 2 * T );
ld v = LDBL_MAX;
int res = -1;
for (int i = max(0ll, x - 1); i <= x + 1; i++) {
ld vi = abs(ld((i + 1) * h + i * c) / ld(2 * i + 1) - ld(T));
if( vi < v ) v = vi , res = 2*i+1;
}
cout << res << "\n";
return;
}
int32_t main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int T;
cin >> T;
while (T--)
solve();
return 0;
}
I - 你爱我不断提升的力量,对吧!
先尽可能的吧小丑给一个人,在把剩下的小丑尽可能的平分,最后计算差值即可。
#include <bits/stdc++.h>
using namespace std;
void solve() {
int n, m, k, t;
cin >> n >> m >> k, t = n / k;
int a = min(m, t);
m -= a, k--;
int b = (m + k - 1) / k;
cout << a - b << "\n";
return;
}
int32_t main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int T;
cin >> T;
while (T--)
solve();
return 0;
}
J - 把德克萨斯放到我的小队来!
首先如果\(n\)是偶数,则\(A,B\)不会相遇。当\(n\)是奇数是,\(B\)每一圈都多走了 1 步,这里的一圈是指圈上所有的点被覆盖过一次,并且每\(\frac{n}{2}\)步可以完整的覆盖一次。所以计算完整覆盖了多少次即可。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int M = (1 << 15) - 1;
void solve() {
int n, k, f;
cin >> n >> k, k -- , f = n / 2;
cout << (k + (n % 2) * (k / f)) % n + 1 << "\n";
}
int32_t main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int t;
cin >> t;
while (t--)
solve();
return 0;
}
K - 让德克萨斯来担任队长!
首先如果没有删除操作的话,实际上就是非常简单最大子串和。
Bob实际上为了最优解,删除的一定子区间的内的最大值。
发现\(a\)的值域其实很小,我们考虑枚举最大值\(k\),然后不选大于最大值的值,即把\(a_i>k\)的值置为\(-\infty\),这样的话,求出最大子串和之后减去\(k\)就是当前情况下的最优解。
#include <bits/stdc++.h>
using namespace std;
#define int long long
int32_t main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int n, res = 0;
cin >> n;
vector<int> a(n);
for (auto &i: a) cin >> i;
for( int k = 0 ; k <= 30 ; k ++ ){
auto b = a;
for( auto & i : b )
if( i > k ) i = INT_MIN;
int ans = 0 , cnt = 0;
for( const auto & i : b ){
cnt += i;
if( cnt < 0 ) cnt = 0;
ans = max( ans , cnt );
}
res = max( res , ans - k );
}
cout << res << "\n";
return 0;
}
L - 小叶,掩护我们离开这里!
首先,当\(n\)为奇数的时候,每只球队需要参与偶数场比赛,所以不用平局,反之每只球队至少产生一次平局。
我们保证球队\(i\)赢\(j\)在\(2(j-i)<n\)即可。
当然本题构造方法有很多种。
#include <bits/stdc++.h>
using namespace std;
#define int long long
void solve(){
int n;
cin >> n;
for( int i = 1 ; i <= n ; i ++ )
for( int j = i+1 ; j <= n ; j ++ ){
if( 2*(j-i) == n ) cout << "0 ";
else if( 2*(j-i) < n ) cout <<"1 ";
else cout << "-1 ";
}
cout << "\n";
return ;
}
int32_t main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int t;
cin >> t;
while( t -- )
solve();
return 0;
}
M - 博士面罩下面是什么样的呢,悄悄看看吧...
用双指针的方式来枚举前缀和后缀,当糖果之和相等时统计答案。
#include <bits/stdc++.h>
using namespace std;
void solve() {
int n;
cin >> n;
vector<int> a(n);
for (auto &i: a) cin >> i;
int res = 0;
for (int l = 0, r = n - 1, cntL = a[0], cntR = a[n - 1]; l < r;) {
if (cntL == cntR)
res = max(res, n - (r - l - 1));
if (cntL <= cntR) l++, cntL += a[l];
else if (cntR < cntL) r--, cntR += a[r];
}
cout << res << "\n";
return;
}
int32_t main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int T;
cin >> T;
while (T--)
solve();
return 0;
}