D、Prefix K-th Max
题目大意:给定一个长度为n的数组和一个k,对于每一个i(i >= k&&i <= n), 求前i个数字中第k大的数字是什么。
题解: 此题可以用优先队列
来解决, 我们可以维护一个小根堆,然后维护其里面的size,一旦大于k则进行pop,这样的话每次输出答案时列头就是我们所需的答案,由于每个值只进来一次只出去一次,因此时间复杂度时O(nlogn) !
AC代码:
#include
#include
#include
#include
#include
using namespace std;
const int N = 5e5 + 10;
int g[N];
int main()
{
int n, k;
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i ++ ) scanf("%d", &g[i]);
priority_queue<int, vector<int>, greater<int> > q; // 小根堆
for (int i = 1; i <= n; i ++ )
{
q.push(g[i]);
if (i >= k)
{
if (q.size() > k) q.pop(); // 必须先进行pop,否则列头就是第k+1大的数
printf("%d\n", q.top());
}
}
return 0;
}
E、Arithmetic Number
题目大意:输入一个n,设di是n第i为的数字,求大于等于x的数里面满足d2-d1=d3-d2=d4-d3=…=dk-dk-1(k为该数的位数)且是最小数是多少
题解:此题可以使用dfs加一些剪枝来完成,我们先把n的每一位给提取出来存放到数组里面(便于后续dfs搜索时实行该位上的数字限制),然后从高位开始往后面搜,这样找到的满足要求的数就一定是最小的数。具体看代码注解。时间复杂度好像是挺低的,这个不太会算复杂度。
还有就是答案的位数一定跟n的位数是相同的, 因为假设n有x位,那么x个9构造出来的数一定沐足是个等差数列的要求,所以答案一定不会比x个9还有大,因此答案的位数一定跟n的位数是相同的
AC代码:
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
ll n, ans;
int g[70], cnt;
// pos是当前搜到第几位, res是最高位到pos + 1位搜到的暂时符合答案的值
// flag1是用来标记存在相同位上是否有resi > xi,如果有,则后续搜索可以从0开始,否则得从g[pos]开始
// flag2是用来表示res是否已经放了两位数, d为公差
bool dfs(int pos, ll res, bool flag1, bool flag2, int d)
{
if (pos == 0) // 说明全部位已经搜索完毕,进入到这一步的一定是答案
{
ans = res;
return true; // 一旦找到答案立刻返回,结束dfs
}
// cout << res << endl;
int up = flag1 ? g[pos] : 0; // flag2控制此处, 保证了造出来的数一定是大于n的
for (int i = up; i <= 9; i ++ ) // 保证了造出来的数一定是大于n的
{
if (res == 0) // 第一位可以任意放
{
if (dfs(pos - 1, i, i == up && flag1, false, 0)) return true;
}
else if (flag2 && i - (res % 10) == d) // 此时存在公差,且i加到res末尾是符合要求的
{
if (dfs(pos - 1, res * 10 + i, i == up && flag1, true, d)) return true;
}
else if (!flag2) // 此时不存在公差,第二位也可以任意放
{
if (dfs(pos - 1, res * 10 + i, i == up && flag1, true, i - res)) return true;
}
}
return false;
}
int main()
{
cin >> n;
while (n) g[++cnt] = n % 10, n /= 10; // 提取n的每一位
dfs(cnt, 0, true, false, 0);
printf("%lld\n", ans);
return 0;
}
F、 Reordering
题目大意:给定一个字符串s,可以从其中任意选出字符串,然后继续任意顺序的组合,问总共能得到多少种字符串,答案对998244353取模。
题解:
此题是用动态规划和组合计数来解决的。我们可以将题目理解为从s中选ai个a, bi个b·····zi个z然后对这些字符串进行排序能得到多少种字符串。
f[i][j]表示对于前i个字母(a~z)组成长度为j的方案数
那么f[i][j] = f[i - 1][j-0] * C(j, j -0) + f[i - 1][j-1]*C(j,j-1) + ···· + f[i - 1][j-k]*C(j,j-k) (k <= min(j, num[i])) num[i] 为字母i在s中的数量。
证明
:在这里我们枚举的是第i个字母的在长度为j的字符串里面放了多少个,假设我们第i个字母放了x个,那么也就是说前i - 1个字母总共放了j - x个,那么相当于我们要将这x个字母i插入到j - x + 1个空隙当中去,因此就有C(j,j - k)种方案插入到这些空隙中去(不懂可以去学一下,也就是n个相同的小球放入到m个不同的盒子里面的方案数是C(n + m -1, m-1)), 而前i - 1种字符组成长度为j的方案数我们已经求得因此 对于每一个x其贡献为f[i - 1][j-x]*C(j,j-x),而f[i - 1][j-x]已经求出来了,因此f[i][j]的值就是这些贡献的和。
AC代码:
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int N = 5010, mod = 998244353;
char s[N];
int f[30][N]; // f[i][j] 表示前i个字母组成长度为j的方案数
int fac[N], inv[N]; // fac[i]为i的阶乘, inv[i]为阶乘i的逆元
int num[30];
int qmi(int a, int b)
{
int t = 1;
while (b)
{
if (b & 1) t = 1ll * t * a % mod;
b >>= 1;
a = 1ll * a * a % mod;
}
return t;
}
void init()
{
inv[0] = inv[1] = fac[1] = fac[0] = 1;
for (int i = 2; i < N; i ++ )
{
fac[i] = 1ll * fac[i - 1] * i % mod; // 阶乘
inv[i] = 1ll * inv[i - 1] * qmi(i, mod - 2) % mod; // 阶乘的逆元
}
}
int getc(int n, int m)
{
if (n < m || n < 0 || m < 0) return 0;
return 1ll * fac[n] * inv[m] % mod * inv[n - m] % mod;
}
int main()
{
scanf("%s", s + 1);
int n = strlen(s + 1);
for (int i = 1; i <= n; i ++ ) num[s[i] - 'a' + 1] ++;
init();
f[0][0] = 1;
for (int i = 1; i <= 26; i ++ )
for (int j = 0; j <= n; j ++ )
for (int k = 0; k <= min(j, num[i]); k ++ )
f[i][j] = (f[i][j] + 1ll * f[i - 1][j - k] * getc(j, j - k) % mod) % mod;
int ans = 0;
for (int i = 1; i <= n; i ++ )
ans = (ans + f[26][i]) % mod;
cout << ans << endl;
return 0;
}
如有错误,还望指正,完结撒花!!!