2022牛客暑期多校训练营9(ABEG)

个人题解,非广告
题集链接

A Car Show

题意

给定长度为n,由数字1~m组成的数列,问包含全部m个数字的子串个数;

思路

定位每一个左端点,向右寻找最近的符合要求的右界,得到最小子串,右界从此处开始直至序列末尾均合法,依此更新答案;

更新答案后左界左移,再次执行以上过程;

代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[100005];
int main()
{
    int n, m;
    cin >> n >> m;
    ll ans = 0;
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
    }
    int l = 1, r = 1, ct = 0;
    unordered_map<int, int> mp;
    for (; r <= n; r++)
    {
        mp[a[r]]++;
        if (mp[a[r]] == 1)
        {
            ct++;
        }
        while (ct == m && l <= r)
        {
            ans += n - r + 1;
            mp[a[l]]--;
            if (!mp[a[l]])
                ct--;
            l++;
        }
    }
    cout << ans;
    return 0;
}

B Two Frogs

概率期望

题意

有n个荷叶排成一列,每个荷叶有属性值 a i a_i ai ,当青蛙在i号荷叶上时,下一步可以跳到 [ i + 1 , i + a i ] [i+1,i+a_i] [i+1,i+ai] 号荷叶上,求两只青蛙从1号荷叶起经过相同次数的跳动,同时到达n号荷叶的概率;

思路

考虑 q [ m ] [ x ] q[m][x] q[m][x] 为跳m次到达x号的概率,则有 q [ m ] [ x ] = ∑ i + 1 ⩽ x ⩽ i + a [ i ] q [ m − 1 ] [ i ] a [ i ] q[m][x]=\sum_{i+1\leqslant x\leqslant i+a[i]}\frac {q[m-1][i]}{a[i]} q[m][x]=i+1xi+a[i]a[i]q[m1][i]
答案即为 ∑ i = 1 n − 1 q [ i ] [ n ] 2 \sum_{i=1}^{n-1}q[i][n]^2 i=1n1q[i][n]2

考虑计算过程,由于对于每一个 q [ m ] [ x ] q[m][x] q[m][x] ,更新的是 q [ m ] [ x + i ] , i ∈ [ 1 , a [ x ] ] q[m][x+i],i\in[1,a[x]] q[m][x+i],i[1,a[x]] 连续区间的值,我们可以用差分优化这个操作;

代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll M = 998244353;
ll q[8003][8003];
int exs[8003];
ll inv[8003];
int a[8003];

long long Q_power(long long a, long long b = M - 2)
{
    long long res = 1;
    while (b)
    {
        if (b & 1)
            res = res * a % M;
        b >>= 1;
        a = a * a % M;
    }
    return res;
}

int main()
{
    int n;
    cin >> n;
    for (int j = 1; j <= n; j++)
    {
        inv[j] = Q_power(j);
    }
    for (int i = 1; i <= n; i++)
        exs[i] = 10000;
    exs[1] = 0;
    for (int i = 1; i < n; i++)
    {
        scanf("%d", &a[i]);
        for (int j = 1; j <= a[i]; j++)
        {
            exs[i + j] = min(exs[i + j], exs[i] + 1);
        }
    }
    q[1][0]=1;
    for (int i = 1; i <= n; i++)
    {
        for (int j = exs[i]; j <= i - 1; j++)
        {
            q[i][j] = q[i - 1][j] + q[i][j];
            q[i][j] %= M;
            ll p = q[i][j] * inv[a[i]];
            q[i + 1][j + 1] = (q[i + 1][j + 1] + p) % M;
            q[i + a[i] + 1][j + 1] = (q[i + a[i] + 1][j + 1] - p) % M;
        }
    }
    ll ans = 0;
    for (int i = exs[n]; i < n; i++)
        ans += q[n][i] * q[n][i], ans %= M;
    printf("%lld", ans);
    return 0;
}

E Longest Increasing Subsequence

构造

题意

构造长度为 n ⩽ 100 n\leqslant100 n100 的排序,使得最长上升子序列恰好为m个;

思路

将m进行二进制拆分,考虑以下构造方法:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
对于每层最右侧的点,其向下的支是累计下层计数,向左的支是累计本层计数;

依照此策略,可以在 3 log ⁡ ( n ) 3\log(n) 3log(n) 的长度内构造完成;

代码
#include <bits/stdc++.h>
using namespace std;
int main()
{
    int n, t;
    cin >> t;
    while (t--)
    {
        int ct = 1;
        vector<int> r(90);
        cin >> n;
        for (int i = 0; i < 30; i++)
        {
            if ((n >> i) & 1)
            {
                r[2 * i + 1] = ct++;
                r[60 + i] = ct++;
                r[2 * i] = ct++;
            }
            else
            {
                r[60 + i] = ct++;
                r[2 * i + 1] = ct++;
                r[2 * i] = ct++;
            }
        }
        printf("90\n");
        for(int i=0;i<90;i++)
        {
            printf("%d%c",r[i]," \n"[i == 89]);
        }
    }
}

G Magic Spells

manacher,二分

题意

给定k个字符串,找出在每个串都出现的回文串数量;

思路

首先考虑马拉车算法,找出所有字符串进行统计,使用map将字符串从哈希值映射到最近出现时间(1~k);

果不其然地T了,接下来考虑优化:

首先进行剪枝,在固定圆心从大到小遍历半径的过程中,如果发现某串最近出现时间已经是当前,那么该圆心下半径更小的串就不用考虑了;

依然T,继续优化:

考虑二分,对于固定圆心从大到小遍历半径的过程中,存在分界半径i,半径小于i时最近出现时间均是上串,大于i时最近出现时间均早于上串,以此二分快速确定有效半径;

单哈希可以过;

代码
#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int maxn = 3e5 + 5;
char s[maxn * 2];
string str;
int d[maxn * 2], len;
void getstr() {
   str.clear();
   int k = 0;
   str += '{';
   k++;
   for (int i = 0; i < len; i++) {
      str += '}';
      str += s[i];
      k += 2;
   }
   str += '}';
   k++;
   len = k;
}
int manacher() {
   int mx = 0, id;
   int maxx = 0;
   for (int i = 0; i < len; i++) {
      if (i < mx)
         d[i] = min(mx - i + 1, d[2 * id - i]);
      else
         d[i] = 1;
      while (i + d[i] <= str.size() && i - d[i] >= 0 &&
             str[i + d[i]] == str[i - d[i]])
         d[i]++;
      if (d[i] + i - 1 > mx) {
         mx = d[i] + i - 1;
         id = i;
         maxx = max(maxx, d[i]);
      }
   }
   return (maxx - 1);
}

const long long MAGIC = 1e12 + 39;
long long hsh[maxn * 2];
void init() {
   hsh[0] = 1;
   for (int i = 1; i != maxn * 2; i++) {
      hsh[i] = hsh[i - 1] * 1013ll % MAGIC;
   }
}

long long str_hsh[maxn * 2];
void _hsh() {
   str_hsh[0] = str[0] - '_';
   for (int i = 1; i != str.length(); i++) {
      str_hsh[i] = (str_hsh[i - 1] * 1013ll + str[i] - '_') % MAGIC;
   }
}

long long get_hash(int l, int r) {
   if (l == 0) return str_hsh[r];
   auto fna = str_hsh[r] -
              __int128_t(1) * str_hsh[l - 1] * hsh[r - l + 1] % MAGIC;
   if (fna < 0) fna += MAGIC;
   return fna % MAGIC;
}

long long _len_hash(long long raw, int len) {
   return raw * (long long)3e5 + len;
}

int main() {
   int k;
   long long ans = 0;
   cin >> k;
   init();
   unordered_map<ll, int> mp;
   for (int kki = 0; kki < k; kki++) {
      cin >> s;
      len = strlen(s);
      getstr();
      _hsh();
      manacher();
      for (int i = 0; i < len; i++) {
         // printf("%c%d",str[i],d[i]);
         for (int j = d[i]; j >= 1; j--) {
            auto hsh = get_hash(i, i + j - 1);

            if (mp[hsh] == kki + 1)
               break;
            else if (mp[hsh] != kki) {
               int l = 1, r = j;

               int res = 0;
               while (l <= r) {
                  j = (l + r) / 2;
                  hsh = get_hash(i, i + j - 1);
                  if (mp[hsh] < kki) {
                     res = j;
                     r = j - 1;
                  } else {
                     l = j + 1;
                  }
               }
               j = res;
               // cerr << "IN binary " << j << '\n';
            } else if (mp[hsh] == kki) {
               mp[hsh] = kki + 1;
               if (mp[hsh] == k &&
                   !(str[i + j - 1] == '}' || str[i + j - 1] == '{'))
                  ans++;
               // break;
            }

            // cerr << str.substr(i, j) << " " << hsh << " " <<
            // mp[hsh]
            //      << endl;

            // printf("*%lld %d\n", mp[hsh],kki);
         }
      }
   }
   printf("%lld\n", ans);
   return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值