CF1768E Partial Sorting - 组合数学 -

题目链接:https://codeforces.com/contest/1768/problem/E

题解:
记 P1 为将 1..2×n 排序, P2 为将 n+1..3×n 排序
首先观察到答案一定不会超过 3( P1P2 之后 1..n 的所有数一定在 1..2n 中,这样只需要 P1 就可以排好,同理 n+1..2n & 2n+1..3n

  • 答案为0的情况:1,2..3n 一共一种
  • 答案为1:2n+1..3n 已经有序,1..2n 只需要一次 P1 即可,同理也有只需要一次 P2 的情况。注意减去重复情况( 1..n & 2n+1..3n 均有序,只需要一次 P1/P2,但是计算了 2 次,还有一个已经有序的情况),这里的答案是 (2n)!n!1

答案为 3 的情况不好统计,考虑先算出答案为 2 的情况,然后用 (3n)! 减一下

  • 答案为 2 :显然对应的操作就是 P1P2 或者 P2P1 ,分别考虑这些对应的是什么情况:
    • P1P2:那么需要满足的条件是:[1,2n] 中含有 1..n ,而且至少含一个 2n+1..3n 中的数,或者 [1,2n]1..2n 的一个乱序排列,而且 [2n+1,3n]2n+1..3n 的一个乱序排列(要注意这里面不能存在一次就能排好序的情况,因此很多“乱序”条件是应该的)。对应的答案:((2nn)×n!1)×((2nn)×n!n!)×n!+(n!1)×((2n)!n!) 解释一下,第一个括号是选出 1~n 的位置并排列好,-1 是因为不能出现的情况是 1~n 在下标也是 1~n 的情况恰好有序了(这样只需要一次 P2 就行了); 第二个括号是利用容斥算出至少含 1 个 2n+1..3n 的数的情况并排列好,最后的 n! 是将 [2n+1,3n] 的数排列好。加号后面的表示的是 [或者] 后面之后的情况。因为是乱序所以需要 -1,然后 [1,2n] 随便排
    • P2P1:考虑如何去掉 P1P2 已经数过的情况,最后得出此处的要求是:[n+1,3n] 中含有 2n+1..3n 所有的数,而且 [2n+1,3n] 至少有 1 个 1..n 的数在 [2n+1,3n] 中(这里就和 P1P2 不存在交集了),答案是:k=1n(nk)×(nnk)×((2n)!(nk)×k!×(2nk)!)。解释也很显然:先取 1..n 中的 k 个数,再挑 n+1..2n 中的 nk 个,现在有了 2n 个数要填到 [n+1,3n] 利用容斥就可以求出至少有一个 1..n 位于 [2n+1,3n] 的方案数(钦定 k 个数都在 [n+1,2n] 中,然后排列一下就行了)
  • 3 的情况就是 (3n)! 减去上面的

代码:

// by SkyRainWind
#include <bits/stdc++.h>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define pii pair<int,int>

using namespace std;

typedef long long ll;
typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f, maxn = 3e6+5;

int n,mod=998244353;

int fac[maxn],inv[maxn];
int pw(int x,int y){
	if(!y)return 1;
	if(y==1)return x;
	int mid=pw(x,y>>1);
	if(y&1)return 1ll*mid*mid%mod*x%mod;
	return 1ll*mid*mid%mod;
}

int C(int x,int y){
	return 1ll*fac[x]*inv[y]%mod*inv[x-y]%mod;
}

signed main(){
	scanf("%d%d",&n,&mod);
	fac[0] = inv[0] = 1;
	for(int i=1;i<=maxn-5;i++)fac[i] = 1ll*fac[i-1]*i%mod;
	inv[maxn-5]=pw(fac[maxn-5],mod-2);
	for(int i=maxn-6;i>=1;i--)inv[i] = 1ll*inv[i+1]*(i+1)%mod;
	
	ll t1 = (2ll*fac[2*n]%mod - fac[n]%mod - 1 + mod)%mod;
	ll t2 = (1ll*fac[n]*C(2*n,n)-1)%mod*(1ll*(C(2*n,n)-1)*fac[n]%mod)%mod*fac[n]%mod;
	(t2 += 1ll*(fac[n]-1)*(fac[2*n]-fac[n]+mod)%mod) %= mod;
	
	for(int k=1;k<=n;k++){
		ll tmp = 1ll*C(n,k)*C(n,n-k)%mod;
		tmp = tmp*(fac[2*n]-1ll*C(n,k)*fac[k]%mod*fac[2*n-k]%mod+mod)%mod*fac[n]%mod;
		(t2 += tmp) %= mod;
	}
	ll t3 = (fac[3 * n] - 1 - t1 + mod - t2 + mod) % mod;
	ll ans = (t1 + 2ll*t2%mod + 3ll*t3%mod) % mod;
	cout<<ans;
	
	return 0;
}
posted @   SkyRainWind  阅读(27)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示
点击右上角即可分享
微信分享提示