AtCoder Grand Contest 054

AGC难度上天,希望自己可以将题目陆陆续续的都给做出来。
这里的题目质量奇高,一定要好好刷起来,这是第一篇
只要是我做出来的题目,都会认真的写题解的~~

A - Remove Substrings

题意:给定一个长度为n的字符串,n的大小为1e5,可以选择任意两个不相同的字符,将这两个字符下标之间所含有的字符串全部消除,问最少多少次操作后可以使字符串成为0串,输出最少次数,否则,输出-1

#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int n;
char c[N];
int main()
{
  cin >> n;
  cin >> c + 1;
  if (c[1] != c[n])
  {
    cout << 1 << endl;
    return 0;
  }
  for (int i = 2; i <= n - 1; i ++ )
    if (c[i] != c[1] && c[i + 1] != c[1])
    {
      cout << 2 << endl;
      return 0;
    }
  cout << "-1" << "\n";
  return 0;
}

B - Greedy Division

题意:给定n个w[i],n的大小为100,w大小也为100,我们可以对这n个数任意排序,然后按照操作1操作,最后是否可以得到使两人分的的w之和相等。如果可以,请输出这种合法方案的方案数,否则输出0
操作1:如果当前甲的和小于等于乙,那么将当前的w加到甲当中,如果当前的甲的和大于乙,就将w分给乙。

分析:假设,现在我们已经将所有的w都分好了,而且恰好甲的w和与乙的w和相等。那么,在这种方案下,甲和乙内部的排序可以是任意的,因为我们模拟操作1,就会将方案还原回去。所以,对于某一种甲乙双方分号w后的方案,可以得到x!乘上(n-x)!个方案。那么我们在求出到底有多少个可以使甲乙均分的方案,就可以得到答案,而这个求的过程,正好是0/1背包的过程。
dp[i][j][k]表示的意义为,选择前i个w,当前体积恰好为j,甲已经选择了k个w的方案数(因为甲和乙选择的个数不同,也是不同的方案,所以不用除以2),走一遍背包DP就可以了。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 105, M = 5005, mod = 998244353;
int n;
int w[N];
int dp[N][M][N];
ll fac[N];
void init()
{
  fac[0] = 1;
  for (ll i = 1; i <= 100; i ++ )
    fac[i] = fac[i - 1] * i % mod;
}
int main()
{
  init();

  cin >> n;
  int sum = 0;
  for (int i = 1; i <= n; i ++ ) cin >> w[i], sum += w[i];
  if (sum % 2)
  {
    cout << 0 << endl;
    return 0;
  }
  sum /= 2;
  // cout << sum << endl;
  dp[0][0][0] = 1;
  for (int i = 1; i <= n; i ++ )
    for (int j = 0; j <= sum; j ++ )
      for (int k = 0; k <= n; k ++ )
      {
        dp[i][j][k] = dp[i - 1][j][k];
        if (j - w[i] >= 0 && k > 0) dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j - w[i]][k - 1]) % mod;
      }
#if 0
  for (int i = 1; i <= n; i ++ )
    for (int j = 0; j <= sum; j ++ )
    {
      printf("i = %d, j = %d\n", i, j);
      for (int k = 0; k <= n; k ++ )
        printf("%d ", dp[i][j][k]);
      puts("");
    }
#endif
#if 0
  for (int i = 0; i <= n; i ++ ) cout << dp[n][sum][i] << " ";
  cout << endl;
#endif
  ll ans = 0;
  for (int i = 1; i <= n; i ++ )
    ans = (ans + 1ll * dp[n][sum][i] * fac[i] % mod * fac[n - i] % mod) % mod;
  cout << ans << "\n";
  return 0;
}

C - Roughly Sorted

不行,题目还是没理解懂,溜了溜了,登题解多一点的时候再写吧,菜鸟.jpg
他人题解
题意:有人接收了一个长度为n的序列,其中n是一个5000大的数,序列是{1, 2, 3, …, n}的一个排列。有人对序列p中的某些相邻元素做了交换,试的这个序列满足条件1
条件1:对于任意的i来说,有最多k个下标,满足1<=j<i但是aj > ai
他做了达到条件1的最少的操作次数
后来,他忘记了初始接收的序列p。给他交换后的序列p‘,找到可能存在的原序列的个数
总结来说,给定一个长度为n的序列,其中n是一个5000大的数,序列是{1,2,3,…,n}的一个排列,问满足条件1的情况下,这个序列可以转移成的序列个数有多少个。
条件1,对于任意的i来说,元素Pi最多有k个逆序对。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值