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个逆序对。