2021年泉州市信息学奥赛集训活动(国庆集训)CSP-S Day1

破门而入(broken)

题面描述

\(Kiana\) 到一个密室中去探险寻宝,该密室中共有 \(n\) 个房间,每个房间里都有一件独特的宝藏,而她此行的目的就是将所有房间中的宝藏全部取走。

​ 可惜的是,密室的每个房间门都上了锁,而门锁的钥匙也放在这些房间中,具体来说,第ii号房间内放着第 \(a_i\) 号房间的门钥匙,每扇门的钥匙有且只有一把,换句话说每个房间内放着的钥匙也是两两不同的。为了进入房间寻宝,\(Kiana\) 可以选择直接暴力破开房门,也可以在拿到钥匙之后用钥匙文明地打开房门,但为了防止密室坍塌,\(Kiana\) 至多只能使用暴力破开 \(k\) 扇门。

​ 聪明的 \(Kiana\) 计算出,房间中放钥匙的可能情况一共有 \(n!\) 种,而她想知道其中有多少种情况,使得自己合理选择暴力破开的门后能够最终成功取走所有房间的宝藏。由于 \(Kiana\) 自己不会算,所以希望你能够帮助她,但最后的答案可能很大,你只需告诉她答案对 \(998244353\) 取模后的结果即可。

输入输出格式

输入格式

第一行包含两个正整数 \(n\)\(k\),分别表示密室中的房间数和 \(Kiana\) 使用暴力能破坏的门数。

输出格式

输出共一行,包含一个非负整数,表示符合条件的情况数对 \(998244353\) 取模后的结果。

输入输出样例

输入样例#1:

3 1

输出样例#1:

2

输入样例#2:

10 5

输出样例#2:

3555161

样例解释

在输入输出样例1中,密室内共有 \(3\) 个房间,而 \(Kiana\) 只能使用暴力打开 \(1\) 扇门,所有可能的情况如下:

\(1\) 号房间放着 \(1\) 号门钥匙、\(2\) 号房间放着 \(2\) 号门钥匙、\(3\) 号房间放着 \(3\) 号门钥匙,此时 \(Kiana\) 没有办法取走所有宝藏

\(1\) 号房间放着 \(1\) 号门钥匙、\(2\) 号房间放着 \(3\) 号门钥匙、\(3\) 号房间放着 \(2\) 号门钥匙,此时 \(Kiana\) 没有办法取走所有宝藏

\(1\) 号房间放着 \(2\) 号门钥匙、\(2\) 号房间放着 \(1\) 号门钥匙、\(3\) 号房间放着 \(3\) 号门钥匙,此时 \(Kiana\) 没有办法取走所有宝藏

\(1\) 号房间放着 \(2\) 号门钥匙、\(2\) 号房间放着 \(3\) 号门钥匙、\(3\) 号房间放着 \(1\) 号门钥匙,此时 \(Kiana\) 可以暴力破开 \(1\) 号门,从 \(1\) 号房间拿到 \(2\) 号钥匙后打开 \(2\) 号门,再从 \(2\) 号房间拿到 \(3\) 号钥匙后打开 \(3\) 号门,取走所有宝藏

\(1\) 号房间放着 \(3\) 号门钥匙、\(2\) 号房间放着 \(1\) 号门钥匙、\(3\) 号房间放着 \(2\) 号门钥匙,此时 \(Kiana\) 可以暴力破开 \(1\) 号门,从 \(1\) 号房间拿到 \(3\) 号钥匙后打开 \(3\) 号门,再从 \(3\) 号房间拿到 \(2\) 号钥匙后打开 \(2\) 号门,取走所有宝藏

\(1\) 号房间放着 \(3\) 号门钥匙、\(2\) 号房间放着 \(2\) 号门钥匙、\(3\) 号房间放着 \(1\) 号门钥匙,此时 \(Kiana\) 没有办法取走所有宝藏

综上所述,共有 \(2\) 种符合条件的情况

数据范围

对于 \(30\%\) 的数据,保证 \(1\le k\le n\le 10\)

对于 \(100\%\) 的数据,保证 \(1\le k\le n\le 3000\)

上面每一档数据中,各有一组数据保证 \(k=1\),还有一组数据保证 \(k=2\)

分析

  • \(Stiring\)
Code
#include <cstdio>

const unsigned long long p = 998244353;

unsigned long long C[3001][3001];
unsigned long long n, k;

int main(void) {
	scanf("%llu %llu", &n, &k);
	
	C[1][0] = 0; C[1][1] = 1;
	for (int i = 2; i <= n; ++i) {
		C[i][0] = 0;
		for (int j = 1; j < i; ++j) {
			C[i][j] = (C[i - 1][j - 1] + (i - 1) * C[i - 1][j] % p) % p;
		}
		C[i][i] = 1;
	}
	unsigned long long ans = 0;
	for (int i = 1; i <= k; ++i) {
		ans = (ans + C[n][i]) % p;
	}
	printf("%llu", ans % p);
	
	return 0;
}

翻转游戏(turn)

题目描述

​ 今天 \(Kiana\) 想出一道字符串题,最好是和回文串相关的那种,由于她不会出题,所以就随手写下了一个字符串 \(S\)

​ 贪玩的 \(Kiana\) 尝试翻转这个字符串的一个连续子串 \(s_ls_{l+1}⋯s_{r−1}s_r\),即进行如下操作:对于字符串\(S=s_1s_2⋯s_{l−1}s_ls_{l+1}⋯s_{r−1}s_rs_{r+1}⋯s_{n−1}s_n\),对其翻转 \(Kiana\) 指定的子串 \(s_ls_{l+1}⋯s_{r−1}s_r\) 后字符串将变为\(S=s_1s_2⋯s_{l−1}s_rs_{r−1}⋯s_{l+1}s_ls_{r+1}⋯s_{n−1}s_n\)。聪明的 \(Kiana\) 还发现,对于长度为 \(n\) 的字符串,一共有 \(\frac{n(n+1)}2\) 个不同的子串可以翻转,真是太有趣了!

​ 但是 \(Kiana\) 意识到,翻转某些子串并不会得到新的子串,而翻转某些不同的子串可能会得到相同的新字符串,所以她想知道,通过翻转 \(S\) 的一个子串,可以得到的不同字符串数量是多少。由于 \(Kiana\) 自己不会算,所以希望你能够帮助她。

输入输出格式

输入格式 第一行包含一个字符串 \(S\),表示 \(Kiana\) 写下的字符串。

输出格式 输出共一行,包含一个正整数,表示翻转 \(S\) 的一个子串可以得到的不同字符串数量。

输入输出样例

输入样例#1:

abcd

输出样例#1:

7

输入样例#2:(爷不配开复制功能(其实就是懒),所以请自·行·手·动·复·制)

abbbaaaabbaabbbaaabaababbabbabaaaaaaaabaaabbaabababbababaaaababbbabaaaababaababaabbabbbaabbaabababbaabaabaabbabbbaabaaaaabaaabaaabbaabaabbaaaabbbabbaabaaabbbabaaaabaabaabaabbabbaaabbbababbbbbaaaaaababababbbbababbaaababbabbaaababbbaabbbaabbbabbbababaabbaabbbaabaaaababbbabbbbabbbabbbbbaabaabaaababaaabbbabaaaaaaaababaabbbbbbbbaaabbababbabaabbbababbaabaabbbaabaabbaabababbabbabbaababbbbbaaababaaabbbbbabbabbbaabbbaabbabbaabbabaabbaabbaabbbaabbabbbbbaaaabbababbabbbbaaabaabbbaababbaabababbaaaaabaaaabbbbabbbabbabbaaabbbabbabbaaabaababbaaaaaaaaabbbbaabaabbaaabaabbbbbbbbabbbbaabbbbbaaababababababbaaaababababbbbabbabbbabaabbbabbbbabbabbbabbabaaaababaaaabaabbbbbaaaaaaaabbaaaabbbaababbbabbbbabaaaaabaaaaabbabaaaaaaabbababaaaaaaababbaaaabaabbbaaaabaabaabaaabbababbbbbbababbabbbbababaabbabaabbbbbbaabaaabbabbaabbbaaabbabbabbbaabbbaabaaaabbbbabbbbaababbabbaabbabbaaabbabbbaabbababbabbbaabbaabababbbbabababbbabbaaaabbaaababbabbabbabbaaabaabbbbbabbabaaaabbbaababbabbabbaaabbbbbaaaaaaabbbaabababaabbbabbaabbbbababbaaaabbbbaababbaaabbabaaabaaababaabaabbaaaabaaababbaaaabaabaababaaaaababbaaabbabbaabbbbbaaaabbbbbbbaabbabbaaaaaaaabbabaaababaaabbabaaaabababbbaaabaaabaaaabbbabbaaabaaaaaaabababaabbbaabbaaaabbbaabbbaababaababbbbabbaaabaaaabbaaabbababbaabbbababaaaaababbbbbbbbbaaaabbbaabbabbabbababbbbbabbaabbbaaaabaabaabbaabbbbaabaabbbbbaaabbbabaababbabbbaabbbbbbbaabaabbbaaabaabaaabbbababaaabbbabbaabbabbaababaabbbbabaaaababbbabaaabababbababaabaabbaaabababbbaabbbbabaaaaabbbabbbaababaabaaabbaaaaaaaaabababbbaaaaabbaaaaabbbaabbaaaaaaabbbabababaaabaaabbaabbbabbbbabbbbbaababbbbabaababbaabbaaaababbaaaaaabaaaaabaabbabbbbbabbaaabbaabbaaaabaaaabaaaaabaabbaabaaaaabbaaaaaaaababaaabbbbbaababaaabbabbaaabbbbbbaaaaabbbabbabbaaaabababbbaaaabbabbabbbabbaabaabbbaaaaaaabbbbaaaabbabbababbbabaabbabaaaaabaabaabaaaaabbbaaabbabbbabbabaabbaaabaaabbbbabababbbbbabbbbbbbbbbaababbbabaabbbbbbbbbaababbbbabbaabaababbabbabbabbababababaabababaabaaabbbbabaabaabbaabbbbaabaaabababbabaabbabbaabaabbbbbabbabaabbaabaaaaabbababbaabaaabbbbaaabaaaabaabaabababaabbaaabbbbabbabaabaababaaaabbbaabbbaabbbbbbbbabaababbabbabaaabbaaaabbbaaaabbbabaababbbabbbababbbbbbbbabbabaabbababaabbaababbbbbaaabbabbbbbbbbbbbbbbabaabababbbbbbbbaabaabbbabababbbbbabbaaaaaaaabbaabaabaababbbbbabbbaaababbaabbabaababbabaaaaabababbabbbbaaabbabaaaabbabbbabbaaabbaaabbabababbbbaabbbbbabbaababbbbbabbabbabbbaaabaaabbbbbabaaabbababaaaabbbaabbabbbbaaaabbabbaaabaabbbabbabbaaaaaabaaaabbbababbbbabaaabaaaaababaaabbbaaaabaababbbabaaaabbbbbaaabbabaaabbbaaabbaabbabbbaabaaabbbbbaabbaaaabaaababbabbbabbaaaaabbbaabbababbbbabaaaaaababbaaabbaabbbbaaabbaabababbbaabbbbbabbabbabbbbaabbbaaababaaabbbabbabbaaabbabbbababbbbabbbaababbbabaaaababababaabbaaababaaabbbbbaabaaaaabbbaabbaaabaabbbbabbabbabbababbabbbaaabbaabaaaaaaaabbaabbaabbbbbaabbbbaabbbbbaabaababaaabbabbabbabaabbbbbbabbbaaaabaaababababaaababaaaabbbbabbbaabaaaabbaaaaabbbabbbaabbbaabbababaabbabaaaaaaababbbabbba

输出样例#2:

2074859

样例解释

在输入输出样例1中,\(Kiana\) 写下的字符串为 \(abcd\),她有 \(10\) 种不同的翻转方法:

翻转子串 \(s_1\),得到字符串 \(abcd\)

翻转子串 \(s_2\),得到字符串\(abcd\)

翻转子串 \(s_3\),得到字符串 \(abcd\)

翻转子串 \(s_4\),得到字符串 \(abcd\)

翻转子串 \(s_1s_2\),得到字符串 \(bacd\)

翻转子串 \(s_2s_3\),得到字符串 \(acbd\)

翻转子串 \(s_3s_4\),得到字符串 \(abdc\)

翻转子串 \(s_1s_2s_3\),得到字符串 \(cbad\)

翻转子串 \(s_2s_3s_4\),得到字符串 \(adcb\)

翻转子串 \(s_1s_2s_3s_4\),得到字符串 \(dcba\)

综上所述,\(Kiana\) 可能得到 \(abcd\),\(bacd\),\(acbd\),\(abdc\),\(cbad\),\(adcb\),\(dcba\),所以输出的答案为 \(7\)

数据范围

对于 \(20\%\) 的数据,保证 \(1\le|S|\le30\)

对于 \(40\%\) 的数据,保证 \(1≤|S|≤300\)

对于 \(60\%\) 的数据,保证 \(1≤|S|≤3000\)

对于 \(80\%\) 的数据,保证 \(1≤|S|≤300000\)

对于 \(100\%\) 的数据,保证 \(1≤|S|≤3×10^6\)

上面每一档数据中,都有 \(25%\) 的数据,保证 \(S\) 中只包含字母 \(a\)\(b\),对于 \(100%\) 的数据,保证 \(S\) 中只含小写字母。

分析

  • 考虑翻转后相同的情况
    • 1、对于翻转单个字母,只能保留一种情况,那么多余的情况就是 字符串的长度-1
    • 2、对于相同的字母,任意两个字母之间的字符串翻转后 和 含有这两个字母的字符串翻转后 一样(如:\(abcda\) 中 只翻转 \(bcd\) 和 翻转 \(abcda\) 得到的情况都是 \(adcba\)),那么多余的情况就是 C(n,2) (其中 \(n\) 为该字母的出现次数)
Code
#include <string>
#include <cstring>
#include <iostream>

typedef long long ll;

std::string s;
/*int*/ll countWord[26]; // 忘开long long了,qwq
ll answer;

int main(void) {
	std::cin >> s;
	
	memset(countWord, 0, sizeof countWord);
	for (auto i : s) {
		++countWord[i - 'a'];
	}
	answer = s.size() * (s.size() + 1) / 2;
	answer -= (s.size() - 1);
	for (auto i : countWord) {
		answer -= (i * (i - 1)) / 2;
	}
	std::cout << answer;
	
	return 0;
}

奶油蛋糕塔(cake)(To be continued)

多重影分身之术(duplication) (To be continued)

posted @ 2021-10-01 19:58  Juro  阅读(624)  评论(0编辑  收藏  举报