23暑假友谊赛 No.3

A - 把你砍成两半!

在本地找规律就会发现,当\(a_1\)确定时,\(a_i\)一定是\(a_1\)的倍数。所以答案就是

\[\sum C_{\frac{n}{a_1}-1}^{k-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 - 唔...这个我真的能做到吗。

其实混合只有两种情况

  1. \(x\)杯热水,\(x\)杯凉水,温度一定\(\frac{h + c }{ 2}\)
  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;
}
posted @ 2023-08-02 17:38  PHarr  阅读(50)  评论(0编辑  收藏  举报