海亮 7.11 模拟赛3

海亮 7.11 模拟赛

大寄特寄

\(T1\) \(20pts\)暴力显然 \(T2\)也有暴搜的分数 然后就不会了

\(T2\)特殊性质没打出来 \(T3\)\(T4\)暴力都寄力()

#A. 计数练习

法一:

我们考虑设\(f[i]\)为答案 那么考虑在\(i-1\)的基础上新加入一个数

分三种情况 对于\(Q\)序列的第一位:

  1. 如果\(Q\)序列的第一位为\(1\) 那么置换后第一位也是\(1\) 只需要满足后续排列即可 计入答案为\(f[i-1]\)

  2. 如果\(Q\)序列的第一位为\(x\) 那么在\(p\)序列中\(x\)位置填上一个\(1\)即可满足

    那么这两个位置已经固定了 其余就是\(f[i-2]\)的排列问题

    为什么不用\((i-2)!\)而用\(f[i-2]\)呢? 因为你更换了这两个值之后 排列后这两个位置和原序列是不变的 其他地方还是需要满足排列的规则

    此时\(x\)的位置有\(i-1\)种 计入答案为\(f[i-2]*(i-1)\)

  3. \(Q\)的第一位不是\(x\) 那么必须满足这一位的值小于\(x\) 值域为\([2,i-1]\) (因为我们值为\(1\)的情况已经计算过了)

    那么此时转化为\(i-2\)个数的排列问题 注意是全排列 因为此时一定满足了\(Q\)序列大于\(p\)序列 那么其他乱排即可

    我们的\(x\)的值域有\(n-2\)种情况 也就是要求\(\sum_{x=3}^{i}(i-2)!*(x-1-2+1)\)

    此时将\((i-2)!\)提出来 答案就是\((i-2)!*\sum_{x=1}^{i-2}x\) 那么对于后面的柿子 等差数列求和即可

    答案就是\(\frac{(i-2)!*(i-2)*(i-1)}{2}\)

所以\(f[i]=f[i-1]+f[i-2]*(i-1)+\frac{(i-2)!*(i-2)*(i-1)}{2}\)

法二:观察题意可以得到 答案即为\(\frac{n!-|Q(p)=p|}{2}\)

其中\(||\)表示这样的排列的个数

那么设置\(f[i]\)\(i\)长度的,\(Q(p)=p\)的排列个数 那么\(f[i]=f[i-1]+f[i-2]*(i-1)\)

法二代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long 
const int N = 1e7 + 5;
inline int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , mod , f[N] , fac[N];

int ksm ( int x , int k )
{
	int base = x , res = 1;
	while(k)
	{
		if ( k & 1 ) res = res * base % mod;
		base = base * base % mod;
		k >>= 1;
	}
	return res;
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , mod = read();
	fac[1] = f[1] = f[0] = 1;
	int ans = 0;
	for ( int i = 2 ; i <= n ; i ++ ) fac[i] = fac[i-1] * i % mod;
	int inv = ksm ( 2 , mod - 2 ); 
	for ( int i = 2 ; i <= n ; i ++ ) f[i] = ( f[i-1] + ( i - 1 ) * f[i-2] % mod ) % mod , ans ^= ( ( mod + fac[i] - f[i] ) % mod * inv + mod ) % mod;
	cout << ans << endl;
	return 0;
}
posted @ 2023-07-11 16:28  Echo_Long  阅读(10)  评论(0编辑  收藏  举报