https://codeforces.ml/contest/1768/problem/E
更新,感谢本人高中同学网名大肥花狗,指正了我文章的错误,已经做出修改。
题意:给你一个n,问你,对于长度为3n的所有排列,要多少次操作才能让全部的排列变成升序。在一次操作中,你可以对前2n个排序,也可以对后面2n个排序。
解法:我们可以将排列分为四种。
第一种:123……3n这个已经升序的了,操作是0次,我们就别管他了。
第二种:需要操作1次的,就是在前2n无序、后n已经排好,或者是后2n无序,前n已经排好的。
第三种:需要操作2次的,前2n无序,后n也无序,但是前n都在前2n里面,所以两次就完事了。反之亦然。
第四种:剩下的组合。
第一次想的时候第二种把排好的想成了有序的就行了,不知道脑子在想什么,很烦。
以k1表示第一种,类推那么ans=3(3n!)-3*k1-2*k2-1*k3-0*k4;
我们在下面求的时候,发现经常k2包括k1等,不用管了,一起减去才是对的。
在开始想怎么求出来这些的数量。
第一种的数量为1,操作数为0。
第二种的数量为2*((2n)!)-n!。n!是指n到2n这一块无序的,都能符合。
第三种的数量嘛,很难想。首先前n个只能放进2n个位置里面,这是一个从2n中选n个的组合,然后这后n个自己排列,前2n个自己排列,因为反过来也一样,所以乘2。但是也重了一部分,就是如果前n个都在前2n里面,后n个也都在n到3n里面。所以要减去(从n取i的组合)*(从n中取n-i的组合)*(从n+i中取n的组合)*(n!)*(n!)*(n!)。(顺便一提,这个算式有对称之美,如果算式拟人化,这位一定是美女。)
到此结束,下面是ac代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 3e6 + 5;
int fect[maxn], infect[maxn];
int m;
int binpow(int a,int b,int m)
{
a %= m;
int res = 1;
while (b > 0) {
if (b & 1) res = res * a % m;
a = a * a % m;
b >>= 1;
}
return res;
}
int C(int a,int b)
{
return fect[a]*infect[b]%m*infect[a-b]%m;
}
void solve()
{
int n;
cin>>n>>m;
fect[0]=1;
infect[0]=1;
for(int i=1;i<=3*n;i++)
{
fect[i]=(fect[i-1]*i)%m;
}
infect[3*n-1]=binpow(fect[3*n-1],m-2,m);
for(int i=3*n-2;i>=1;i--)
infect[i]=infect[i+1]*(i+1)%m;
int cnt0=1;
//第二种
int cnt1=2*fect[2*n]-fect[n];
//第三种
int cnt2=C(2*n,n)*fect[n]%m*fect[2*n]%m*2%m;
int temp=fect[n]*fect[n]%m*fect[n]%m;
for(int i=0;i<=n;i++)
{
cnt2-=C(n,i)%m*C(n,n-i)%m*C(n+i,n)%m*temp%m;
}
cnt2%=m;
int cnt3=fect[3*n];
int ans=(((cnt3*3-cnt2-cnt1-cnt0)%m)+m)%m;
cout<<ans<<"\n";
}
signed main()
{
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
// int t;
// cin>>t;
// while(t--)
solve();
}