海亮 7.11 模拟赛3
海亮 7.11 模拟赛
大寄特寄
\(T1\) \(20pts\)暴力显然 \(T2\)也有暴搜的分数 然后就不会了
\(T2\)特殊性质没打出来 \(T3\)\(T4\)暴力都寄力()
#A. 计数练习
法一:
我们考虑设\(f[i]\)为答案 那么考虑在\(i-1\)的基础上新加入一个数
分三种情况 对于\(Q\)序列的第一位:
-
如果\(Q\)序列的第一位为\(1\) 那么置换后第一位也是\(1\) 只需要满足后续排列即可 计入答案为\(f[i-1]\)
-
如果\(Q\)序列的第一位为\(x\) 那么在\(p\)序列中\(x\)位置填上一个\(1\)即可满足
那么这两个位置已经固定了 其余就是\(f[i-2]\)的排列问题
为什么不用\((i-2)!\)而用\(f[i-2]\)呢? 因为你更换了这两个值之后 排列后这两个位置和原序列是不变的 其他地方还是需要满足排列的规则
此时\(x\)的位置有\(i-1\)种 计入答案为\(f[i-2]*(i-1)\)
-
\(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;
}