2021"MINIEYE杯"中超(5)补题

2021"MINIEYE杯"中超(5)

1004 Another String

维护这么一个数组:f[i, j]表示以i,j为起点的两个子序列可以满足条件的最大长度。那么当t在i ~ i + f[i, j]这个区间时,f[i, j]对答案是有贡献的。当t在j及以后时,f[i, j]对答案是没有贡献的。所以我们可以在求出f[i, j]后,维护一个差分矩阵。对于每个f[i, j],ans[i] ++ , ans[i + f[i,j]] -- ;这表示在这个范围内f[i, j]的贡献,同时当分割点大于等于j时是没有贡献的。所以我们还要有 ans[j] -= f[i, j],ans[j + 1] += f[i, j]来消除此部分的贡献。

最后取两次前缀和即可得到答案。

#include <bits/stdc++.h>

using namespace std;

const int N = 3010;

int t;
int f[N][N];//f[i][j]表示以i,j为起点的两个子序列满足条件的最大长度
int n, k;
char s[N];
int ans[N];

void solve()
{
    cin >> n >> k;
    cin >> (s + 1);
    
    //尺取
    for (int l = 2; l <= n; l ++ )//枚举右端点
    {
        int len = 0, dif = 0;//len为当前满足的最长区间长度,dif为当前不同的位置有多少个
        for (int i = 1, j = l; j <= n; i ++ , j ++ )//从两个区间的起点开始扫两个子序列
        {
            while (dif <= k)//当满足条件的话继续往后移动
            {
                dif += (s[i + len] != s[j + len]);
                len ++ ;
            }
            f[i][j] = min(len - 1, min(j - i, n - j + 1));//更新f[i][j]
            len -- ;//后移一位
            dif -= (s[j] != s[i]);
        }
    }
    
    memset(ans, 0, sizeof ans);
    //差分矩阵更新
    for (int i = 1; i <= n; i ++ )
        for (int j = i + 1; j <= n; j ++ )
        {
            ans[i] ++ ; ans[i + f[i][j]] -- ;
            ans[j] -= f[i][j]; ans[j + 1] += f[i][j];
        }
    for (int i = 1; i <= n; i ++ ) ans[i] += ans[i - 1];
    for (int i = 1; i <= n; i ++ ) ans[i] += ans[i - 1];
    for (int i = 1; i < n; i ++ ) cout << ans[i] << endl;
}

int main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin >> t;
    while (t -- ) solve();
    return 0;
}
1006 Cute Tree

模拟

#include <bits/stdc++.h>

using namespace std;

const int N = 2e5 + 10;

int t;
int n;
int a[N];
struct node
{
    int a[3];
}tr[N << 2];
int cnt;

void build(int ro, int l, int r)
{
    if (l == r) return;
    if (r == l + 1)
    {
        build(tr[ro].a[0] = ++ cnt, l, l);
        build(tr[ro].a[1] = ++ cnt, r, r);
    }
    else
    {
        int k = (r - l) / 3 + 1;
        int b = l + k - 1;
        int c = b + r >> 1;
        build(tr[ro].a[0] = ++ cnt, l, b);
        build(tr[ro].a[1] = ++ cnt, b + 1, c);
        build(tr[ro].a[2] = ++ cnt, c + 1, r);
    }
}

int main()
{
    scanf("%d", &t);
    while (t -- )
    {
        scanf("%d", &n);
        for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
        cnt = 1;
        build(1, 1, n);
        printf("%d
", cnt);
    }
    return 0;
}
1007 Banzhuan

比赛时由于我的原因想错了最优情况,导致思路错误,没有过掉题。。。

最大情况:往第n层放满,然后让它掉下去,放n次。

最小情况:XOY,XOZ,YOZ层放满。

比较坑的地方就是取模操作,由于有除法,直接相除取模会导致错误,所以要用到逆元。我们可以快速幂求得逆元。

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;

const LL MOD = 1e9 + 7;

int t;
LL n;

LL qmi(LL a, LL k)//快速幂求逆元
{
    LL res = 1;
    while (k)
    {
        if (k & 1) res = (LL)res * a % MOD;
        k >>= 1;
        a = (LL)a * a % MOD;
    }
    return res;
}

void solve()
{
    cin >> n; n %= MOD;
    LL a, b, c;
    a = (n % MOD * n % MOD) * (n + 1) % MOD * (n + 1) % MOD * (2 * n + 1) % MOD * qmi(12,MOD - 2) % MOD;
    b = (n - 1) % MOD * (n - 1) % MOD * (n + 2) % MOD * (n + 2) % MOD * qmi(4,MOD - 2) % MOD;
    c = (n - 1) % MOD * n % MOD * (n + 1) % MOD * (n + 2) % MOD * (2 * n + 1) % MOD * qmi(12,MOD-2) % MOD - (n + 2) % MOD * (n - 1) % MOD * qmi(2,MOD - 2)%MOD;
    LL r1 = ((a + b + c) % MOD + MOD) % MOD;
    LL r2 = n * n % MOD * n % MOD * (n + 1) % MOD * (n + 1) % MOD * (2 * n + 1) % MOD * qmi(12, MOD - 2) % MOD * n % MOD;
    cout << r1 << endl << r2 << endl;
}

int main()
{
    cin >> t;
    while (t -- ) solve();
    return 0;
}

原文地址:https://www.cnblogs.com/scl0725/p/15135593.html