比赛题目训练系列13 (2020ICPC·小米 网络选拔赛热身赛)

比赛题目训练系列13 (2020ICPC·小米 网络选拔赛热身赛)

训练网址

A. ABBA

  • 题意:在这里插入图片描述
  • 起点是 ( 0 , 0 ) (0, 0) (0,0),终点是 ( N + M , N + M ) (N + M, N + M) (N+M,N+M) 假设当前已经选了 x x x A A A y y y B B B. 那么当前字符串符合题意当且仅当 − n ≤ y − x ≤ m -n \le y - x \le m nyxm. 因此,我们将所有最后一次从非法区域(下图空白部分)到合法区域(阴影部分)是从 y = x + n y = x + n y=x+n 的转移过来的非法路径(将该转移点记作 M M M),把所有在 y = x + n y = x + n y=x+n 上方的部分,以及M到终点的路径,做关于 y = x + n y=x+n y=x+n 的对称。
  • 这样,所有这样的非法路径就等价于到 C 2 ( n + m ) n + m − C 2 ( n + m ) n − 1 − C 2 ( n + m ) m − 1 C_{2(n+m)}^{n+m} - C_{2(n+m)}^{n - 1}-C_{2(n+m)}^{m - 1} C2(n+m)n+mC2(n+m)n1C2(n+m)m1.
  • 其实很多时候这种题的套路都是做终点关于合法直线向上(向下)平移一格的对称点。
    在这里插入图片描述
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 4010;
const ll mod = 1e9 + 7;
int N, M;
ll fact[maxn], infact[maxn];
ll mod_pow(ll x, ll n)
{
    ll res = 1;
    while(n){
        if(n & 1) res = res * x % mod;
        x = x * x % mod;
        n >>= 1;
    }
    return res;
}
ll C(ll n, ll m)
{
    if(m == -1) return 0;
    return fact[n] * infact[m] % mod * infact[n - m] % mod;
}
int main()
{
    fact[0] = infact[0] = 1;
    for(ll i = 1; i < maxn; i++){
        fact[i] = fact[i - 1] * i % mod;
        infact[i] = infact[i - 1] * mod_pow(i, mod - 2) % mod;
    }
    
    while(cin >> N >> M){
        ll ans = ((C(2 * (N + M), N + M) - C(2 * (N + M), N - 1) - C(2 * (N + M), M - 1)) % mod + mod) % mod;
        printf("%lld\n", ans);
    }

    return 0;
}

B. Beauty Values

  • 题意:求每个子区间不同数的数目之和
  • 思路:其实就是枚举每一个数在那些区间会出现。如果当前第 i 个数是从未出现过的数字,那么包含这个数字的子区间的左端点就是 [ 1 : i ] [1:i] [1:i],右端点就是 [ i : n ] [i:n] [i:n]。因此对答案的贡献就是 i ∗ ( n − i + 1 ) i * (n - i + 1) i(ni+1). 如果之前出现过,出现的位置是 u,那么左端点是 [ u + 1 : i ] [u + 1:i] [u+1:i],右端点是 [ n − i + 1 ] [n - i + 1] [ni+1].
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100010;
typedef long long ll;
int a[maxn], vis[maxn], N;
int main()
{
    scanf("%d", &N);
    for(int i = 1; i <= N; i++){
        scanf("%d", &a[i]);
    }
    ll ans = 0;
    for(ll i = 1; i <= N; i++){
        if(!vis[a[i]]) ans += i * (N - i + 1);
        else{
            ans += (ll)(N - i + 1) * (i - vis[a[i]]);
        }
        vis[a[i]] = i;
    }
    printf("%lld\n", ans);
    return 0;
}

F. Fraction Comparision

  • 签到,注意数据范围别爆long long.
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
ll x, a, y, b;
int main()
{
    while(cin >> x >> a >> y >> b){
        ll num1 = x / a, num2 = y / b;
        x %= a, y %= b;
        if(num1 > num2) printf(">\n");
        else if(num1 < num2) printf("<\n");
        else{
            if(x * b > a * y) printf(">\n");
            else if(x * b < a * y) printf("<\n");
            else printf("=\n");
        }
    }
    return 0;
}

G. Gemstones

  • 题意:可以取出一个序列中连续的三个一样的字符,然后把剩下的序列两部分合成一个。
  • 用一个栈就可以了。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100010;
char stk[maxn], str[maxn];
int N;
int main()
{
    scanf("%s", str + 1);
    N = strlen(str + 1);
    int tt = 0;
    int ans = 0;
    for(int i = 1; i <= N; i++){
        stk[++tt] = str[i];
        if(tt >= 3 && stk[tt] == stk[tt - 1] && stk[tt - 1] == stk[tt - 2]){
            tt -= 3;
            ans++;
        }
    }
    printf("%d\n", ans);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值