Codeforces Round #663 (Div. 2) C - Cyclic Permutations

Codeforces Round #663 (Div. 2) C - Cyclic Permutations
题目链接:https://codeforces.com/contest/1391/problem/C
满足条件的排列数等于总排列数-不满足条件的排列数。
而不满足条件的排列数通过找规律:
如n = 4时:
不满足条件的排列有:
1234
4321
2431
1342
3421
1432
2341
一共八个,但可以分为4组。一位第1,2,第3,4,第5,6,第7,8个排列,分别是reverse的。那么只分析第1,3,5,7个排列。我们发现。其实规律只与排列中的最小数字1和最大数字n的位置有关。
如将排列写成1…n…形式。则1与n之间的数字必须升序,n后的数字必须降序。那么我们只需考虑1与n之间数字的个数即可。
而1与n之间数字的个数取值范围是[0,n-2],假如1与n之间放 i 个数(i <= n-2),又因为这m个数一定是升序,所以是组合数C(n-2,i)。
那么可见,不满足条件的排列数为2*(C(n-2, 0)+C(n-2, 1)+C(n-2, 2)+…+C(n-2, n-2) )= 2* 2^ n-2 = 2^ n-1 。
而全排列个数为n!,所以答案就是n! - 2^ n-1 。
这个公式推导出来并不难。难的是取模。首先快速幂要取模,板子记住就行了没问题。阶乘取模也没问题。重点是我们需要注意一个问题,不同编译器对取模解释不一样,比如 3%5 = 3,(商0)这是我们一般认知。但还可能计算成 3%5 = -2 (商1, 3 = 5*1+(-2))。所以即使我们口算取模后没有负数,但我们不能保证编译器就不给我们运算成负数。所以但凡取模,在最后的结果中,一定要来一步加模取模。使最后的值恒为正数。例如,最后取模计算结果为ans,那输出一定要写成(ans+mod)%mod,mod为模。因为即使是负数,那么负数的最小值也大于-mod,所以加一个mod就必为正数,而取模是为了防止原本就是正数,再加上一个模后大于模。所以加模取模的目的就是使最后结果的值域为[0,mod),mod为模。

#include 
#include 
using namespace std;
const long long mod = 1000000007;
 
long long powl(long long x,long long y){
	if(y == 0) return 1;
	if(y == 1) return x;
	if(y%2 == 1) return powl(x*x%mod,y/2)*x%mod;
	else return powl(x*x%mod,y/2)%mod;
}
 
int main(){
	long long n;
	cin >> n;
	long long ans = 1;
	for(long long i = 2;i <= n;i++){
		ans = (ans*i)%mod;
	}
	long long m = n-2;
	long long sum = powl(2,m)%mod*2%mod;
	cout << (ans-sum+mod)%mod << endl;//重中之重!!!
	return 0;
}

如果 cout << (ans-sum+mod)%mod << endl;//重中之重!!!这句不加模取模的话,就会像我昨晚打比赛一样,本来是必胜态,打成了必败态:
Codeforces Round #663 (Div. 2) C - Cyclic Permutations_第1张图片本来可以有一个小时做D题,从时间上看是可以做出来的。但因为没有取模加模,结果改了一个多小时,交了12发都不过,今天早上突然想起来没有取模加模,然后加上取模加模交一发就AC了。一次不注意细节,rating又掉96分。不过好在没去见div3。写此博客,谨记细节。虽然昨晚比赛失败了,但失败就要失败的更有意义。我也应该庆幸,好在是提前发现错误,为区域赛避了坑2333。继续努力,争取下次一场比赛把rating打回来。

你可能感兴趣的:(ACM-ICPC算法,数论,codeforces)