洛谷 P5684 讲解(C++)

传送门

目录

思路:

代码:


思路:

        这道题让我们求非回文串的个数,但很不好求。所以,我们可以先求出回文串个数,再用总

排列个数减去回文串个数,就能得到非回文串个数。

        总排列个数很容易求,即 n * (n - 1) * (n - 2) * ...... * 2 * 1。所以,这道题难点就在于如何求回

文串个数。

        如果一个字符串是回文串。那么,它的左边也就等于它的右边。我们只需要求出一边的组合

个数,即 (n / 2) * (n / 2 - 1) * ...... * 2 * 1。就是回文串排列个数。

        但是,如果有字母相等,又该怎么解决?

        如果有字母相等,我们应求出其排列个数 (设其为 num) ,即 num * (num - 1) * ...... * 2 * 1。

但是,有可能会重复。举个例子:

        假如有 4 个字母 b 。求出组合个数自然没有问题。但是,在之前,我们已经求出回文串组合

个数,就会出现被重复计算。所以,我们需要去除被重复计算部分,即出去 (num / 2) * (num / 2

- 1) * ...... * 2 * 1。读者可以自行思考为什么要出去这些数。 

         最后,在计算重复字母时,我们只需要计算 num * (num - 1) * ...... *(num / 2 + 2) *(num / 2 + 1)。

代码:

#include<bits/stdc++.h>
using namespace std;
long long lis[29]; //桶,记录字母出现个数 
const long long maxn=1e9+7; //取模数字: 
int main(){
	long long n,ans=1;
	char c;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>c;
		lis[c-'a']++; //增加该字母个数 
	}
	int ji=0,xb=0;
	for(int j=0;j<26;j++) ji+=lis[j]%2; //计算出现奇数个字母个数 
	for(int i=2;i<=n;i++) ans=(ans*i)%maxn; //计算总排列个数 
	if(ji>1){ //如果奇数个字母个数超过一个,没有回文串,直接输出 
		cout<<ans;
		return 0;
	}
	long long num=1;
	for(int i=1;i<=n/2;i++) num=(num*i)%maxn; //计算回文串排列个数 
	for(int i=0;i<26;i++){ 
		//计算字母相等个数 
		for(int j=lis[i]/2+1;j<=lis[i];j++) num=(num*j)%maxn;
	}
	ans=(ans-num+maxn)%maxn; //得到答案(ans-num可能为负数) 
	cout<<ans;
	return 0;
} 

  谢谢大家的阅读!

<think>嗯,用户问的是如何用C++解决的P4305题目。我需要先回忆一下这道题的具体内容。P4305的题目名称是“[JLOI2011]不重复数字”,对吧?这道题的要求应该是给定一组数,输出其中不重复出现的数字,保持它们在原序列中的第一次出现顺序。也就是说,去重但保留顺序,而不是简单地使用集合或者排序处理。 首先,用户可能需要了解如何高效地处理这个问题。因为输入的数据量可能比较大,所以普通的暴力方法可能会超时。比如,对于每个元素遍历之前的所有元素检查是否重复,这样的时间复杂度是O(n²),对于大数据量肯定不行。所以需要考虑更高效的方法,比如使用哈希表来记录已经出现过的元素,这样每次查找的时间是O(1)。 接下来,用户可能需要具体的实现步骤。比如,如何读取输入,如何处理多组测试用例。题目通常会有多组测试数据,每组数据包含一个数字n,然后是n个整数。所以程序需要先读取测试用例的数量,然后循环处理每组数据。 然后,关于数据结构的选择。C++中的unordered_map或者unordered_set可以用来快速判断元素是否已经存在。但需要注意的是,unordered_set在插入和查询上的平均时间复杂度是O(1),而遍历的时候需要保持元素的插入顺序。但这里需要保留的是原序列中的第一次出现的顺序,所以应该按照原顺序遍历每个元素,将未出现过的元素加入结果列表,并标记为已出现。 具体的步骤可能是这样的:对于每组测试数据,创建一个空的vector或者数组来保存结果,同时创建一个unordered_set来保存已经出现的元素。然后遍历输入的每个数字,如果该数字不在unordered_set中,就将其加入结果vector,并插入到unordered_set中;否则跳过。最后输出结果vector中的元素,用空格分隔。 需要注意的是,输入输出的效率问题。因为数据量可能很大,使用cin和cout可能会比较慢,这时候可以考虑使用scanf和printf,或者关闭同步流来加速输入输出。比如在C++中使用ios::sync_with_stdio(false); cin.tie(0);这样可以提高速度。 另外,关于测试数据中的数字范围,题目中的数字可能比较大,比如绝对值不超过10^9,所以用int可能足够,但要看具体的情况。不过C++中的int通常是32位,可以处理到2e9左右,所以应该没问题。但如果是更大的数可能需要用long long,但题目里应该不会有这种情况。 可能的错误点包括:处理多个测试用例时,没有清空之前的集合和结果数组,导致不同测试用例之间的数据混淆。所以在每个测试用例处理前,必须清空结果vector和unordered_set。 还有一个点,输出的最后一个数字后面不能有空格,否则可能导致格式错误。所以在输出的时候需要控制,比如先输出第一个元素,然后后面的元素前面加空格,或者用join函数来拼接字符串。不过在C++中,可以逐个输出元素,第一个之后每个前面加空格,或者在循环中处理。 另外,关于输入的方式,因为每组测试用例的n之后有n个数字,所以需要正确读取每个数字。需要注意的是,有时候输入中可能存在多个空格或者换行符分隔,但C++的cin会自动处理这些,所以用循环读取n次即可。 可能的测试用例是,比如输入: 3 1 2 2 输出应该是1 2。或者输入: 5 1 2 3 2 1,输出是1 2 3。 总结一下,解决步骤是: 1. 读取测试用例数T。 2. 对于每个测试用例: a. 读取n。 b. 读取接下来的n个数字,保存到一个临时数组或直接处理。 c. 遍历这些数字,使用unordered_set判断是否已经存在,若不存在则加入结果vector,并插入到集合中。 3. 输出结果vector中的元素,注意格式。 在代码实现中,需要注意输入输出效率,使用快速的方法。此外,还要处理每组测试用例的初始化,避免数据残留。 例如,代码的大致结构可能是: #include <iostream> #include <unordered_set> #include <vector> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int T; cin >> T; while (T--) { int n; cin >> n; vector<int> nums(n); for (int i = 0; i < n; ++i) { cin >> nums[i]; } unordered_set<int> seen; vector<int> res; for (int num : nums) { if (seen.find(num) == seen.end()) { res.push_back(num); seen.insert(num); } } for (int i = 0; i < res.size(); ++i) { if (i > 0) cout << ' '; cout << res[i]; } cout << '\n'; } return 0; } 这样应该就能正确处理了。但需要测试一下边界情况,比如n=0?不过题目中的n应该至少是1。另外,当所有元素都重复时,结果是否正确。比如输入全是2,则输出一个2。 可能还需要注意的是,unordered_set在某些情况下可能会有哈希冲突,导致性能下降,但对于题目中的时间限制来说,一般情况下这种方法应该可以通过。如果遇到更严格的时间限制,可能需要用更快的哈希结构,或者改为使用数组或位图,但题目中的数字范围很大,所以只能用哈希表。 总之,核心思路就是用哈希表来记录已出现的元素,保持顺序的同时去重。</think>下面分步讲解如何使用C++解决P4305 "[JLOI2011]不重复数字": ### 题目分析 给定$n$个整数,保留每个数字**第一次出现的顺序**并去重。例如输入`1 2 2 3 3 1`,输出`1 2 3`。 ### 核心思路 1. **哈希表判重**:用`unordered_set`记录已出现的数字 2. **顺序保留**:遍历原始数组时,按顺序收集首次出现的数字 3. **输入输出优化**:关闭同步流加速IO ### 代码实现 ```cpp #include <iostream> #include <unordered_set> #include <vector> using namespace std; int main() { // 优化输入输出(关键步骤) ios::sync_with_stdio(false); cin.tie(nullptr); int T; cin >> T; // 读取测试用例数量 while (T--) { int n; cin >> n; // 当前用例的数字个数 vector<int> nums(n); for (int i = 0; i < n; ++i) { cin >> nums[i]; // 读取所有数字 } unordered_set<int> seen; // 哈希表判重 vector<int> result; // 保存结果 for (int num : nums) { if (!seen.count(num)) { // 首次出现 result.push_back(num); seen.insert(num); } } // 格式化输出结果 for (size_t i = 0; i < result.size(); ++i) { if (i > 0) cout << " "; // 数字间用空格分隔 cout << result[i]; } cout << "\n"; // 换行符结束当前用例 } return 0; } ``` ### 关键点说明 1. **复杂度控制**: - 时间复杂度:$O(n)$,每个元素仅被处理一次 - 空间复杂度:$O(n)$,哈希表和结果数组各存储n个元素 2. **输入输出优化**: ```cpp ios::sync_with_stdio(false); // 解除C与C++IO同步 cin.tie(nullptr); // 解除cin与cout的绑定 ``` 这两行代码可使IO速度提升约10倍,有效防止大数据超时 3. **格式处理技巧**: - 输出时第一个元素前不加空格 - 后续元素前添加空格 - 行末用换行符`\n`代替`endl`避免频繁刷新缓冲区 ### 测试用例演示 输入: ``` 3 11 1 2 3 4 5 6 7 8 9 10 1 5 1 1 1 1 1 6 1 2 3 2 3 4 ``` 输出: ``` 1 2 3 4 5 6 7 8 9 10 1 1 2 3 4 ``` ### 易错点提醒 - 忘记处理多组测试数据 - 输出格式末尾多空格 - 未关闭同步流导致IO超时 - 未及时清空哈希表和结果数组(本代码通过局部变量自动处理)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值