AtCoder Beginner Contest 234 (D-F)

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;
}

如有错误,还望指正,完结撒花!!!

你可能感兴趣的:(深度优先,算法,数据结构)