首页 > 其他分享 >题解 BZOJ3622【已经没有什么好害怕的了】

题解 BZOJ3622【已经没有什么好害怕的了】

时间:2023-01-11 12:00:59浏览次数:59  
标签:ifac int 题解 LL 害怕 leq fac BZOJ3622 2010

前置知识:二项式反演

problem

两个 \(n\leq 2000\) 的数组 \(A,B\),元素互不相同,求有多少种将 \(A,B\) 配对的方法使得 \(A>B\) 的恰好有 \(k\) 对。

题目改过,但是这一步转换很简单。

solution

恰好很难算。我们算至少:\(g(t)\) 表示钦定 \(t\) 个 pair 是大于有多少种方案,\(f(t)\) 表示恰好有 \(t\) 个 pair 是大于有多少种方案。

二项式反演:

\[g(k)=\sum_{k\leq i\leq n}\binom{i}{k}f(i)\Leftrightarrow f(k)=\sum_{k\leq i\leq n}(-1)^{i-k}\binom{i}{k}g(i). \]

但是这个题的难点不在这里,我们要算 \(g\),怎么算呢?DP。

首先 \(A,B\) 排序对结果无影响,然后二分求出 \(h_i=\sum_j[b_j<a_i]\) 表示有多少个 \(b\) 小于 \(a\)。

令 \(f_{i,j}\) 表示考虑到 \(a_i\),前面有 \(j\) 个已经匹配。

  • \(i\) 不匹配:\(f_{i-1,j}\)。
  • \(i\) 匹配:\((h_i-j+1)f_{i-1,j-1}\),这表示 \(i\) 的匹配对象中,\(j-1\) 个已经被用了,其他的随便挑一个。

然后剩下的随机匹配,得到 \(g(t)=f_{n,j}(n-j)!\)。

code

点击查看代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr,##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
const int P=1e9+9;
LL mod(LL x){return (x%P+P)%P;}
void red(LL&x){x=mod(x);}
LL qpow(LL a,LL b,int p=P){LL r=1;for(a%=p;b;b>>=1,a=a*a%p) if(b&1) r=r*a%p; return r;}
template<int N,int P> struct C_prime{
	LL fac[N+10],ifac[N+10];
	C_prime(){
		for(int i=fac[0]=ifac[0]=1;i<=N;i++) fac[i]=fac[i-1]*i%P;
		ifac[N]=qpow(fac[N],P-2,P);
		for(int i=N-1;i>=1;i--) ifac[i]=ifac[i+1]*(i+1)%P;
	}
	LL operator()(int n,int m){return n>=m?fac[n]*ifac[n-m]%P*ifac[m]%P:0;}
};
int n,k,a[2010],b[2010],h[2010],g[2010];
LL f[2010][2010],fac[2010],ans;
C_prime<2010,P> C;
void dp(){
	f[0][0]=1;
	for(int i=1;i<=n;i++){
		for(int j=0;j<=i;j++){
			f[i][j]=f[i-1][j];
			if(j&&h[i]>=j-1) red(f[i][j]+=f[i-1][j-1]*(h[i]-j+1));
		}
	}
	for(int j=0;j<=n;j++) g[j]=f[n][j]*fac[n-j]%P;
}
int main(){
	for(int i=fac[0]=1;i<=2000;i++) fac[i]=fac[i-1]*i%P;
//	#ifdef LOCAL
//		freopen("input.in","r",stdin);
//	#endif
	scanf("%d%d",&n,&k);
	if((n+k)&1) return puts("0"),0;
	k=(n+k)/2;
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=n;i++) scanf("%d",&b[i]);
	sort(a+1,a+n+1),sort(b+1,b+n+1);
	for(int i=1;i<=n;i++) h[i]=lower_bound(b+1,b+n+1,a[i])-b-1;
	dp();
	for(int i=k;i<=n;i++) red(ans+=C(i,k)*((i-k)&1?-1:1)*g[i]);
	printf("%lld\n",ans);
	return 0;
}
//2x-n=k => x=(n+k)/2
//f[i][j]=f[i-1][j]+f[i-1][j-1]*(h[i]-(j-1))

标签:ifac,int,题解,LL,害怕,leq,fac,BZOJ3622,2010
From: https://www.cnblogs.com/caijianhong/p/solution-bzoj3622.html

相关文章

  • Magic题解
    Magic题解题意简述:给定\(n\)个数\(a_1,a_2,…,a_n\),设对于数\(x\),\(|x|\)表示其在十进制下的位数,也即\(10^{|x|}\lex<10^{|x|+1}\)需要计算:\[\sum_{i=1}^n\sum_{j=i+1......
  • 选数 题解
    选数题解首先,设最初取值为\(x\),按照套路,我们设异或前缀和:\(pre_i=a_1\oplusa_2…\oplusa_i\),设\(f(x)=\left(\left\lfloor\frac{2x}{2^n}\right\rfloor+2x\right)\bmod......
  • Codeforces Round #843 (Div. 2) 题解
    A题目大意给你一个只含字母a,b字符串,要把它拆分成三段,使得其中间那段要么同时小于等于两边要么同时大于等于两边。题解由于只有a,b我们可以分讨解决如果\([2,......
  • CF1783 A-F 题解
    比赛链接:https://codeforces.com/contest/1783连续三场出4题,还行(其实感觉有两场的E都是会做的)A水题//bySkyRainWind#include<bits/stdc++.h>#definemprmake_pai......
  • 「JOI Open 2022」Giraffes 题解
    设我们将要给出的观感好的排列为\(q\),我们希望求出\(\sum[p_i=q_i]\)的最大值(这里指不移动的长颈鹿个数)。结论一:当且仅当左右端点有当前区间最大值或者最小值时条件才......
  • 题解1
    离散化1.为什么要离散化当数据很大的时候,以至于我们不能直接使用它的时候,就要考虑将其用另外一种形式表达,通常是将其映射为数组下标。2.离散化本质本质:映射3.离......
  • charls抓包的乱码问题解决
    1.在charls的安装目录下,去修改配置文件的值----Charles.ini,内容如下   2.SSLproxysetting设置如下图  3.顺便说一下,charls抓取https的包,一定要先安......
  • docker中crontab无法执行导入计划任务问题解决
    问题描述:crontab无法执行导入计划任务解决: ⊙查看文件16进制 hexdump-c./crontab/defalut   发现有\r;crontab中只能直接\n⊙vim文件修改编码   setfile......
  • P3521 题解
    非线段树合并做法。复杂度多一只log,但是好写。跳过不重要的部分,直达核心——如何在递归时计算两棵子树互相的贡献?题解区清一色线段树合并从值域角度考虑。但是显然倍......
  • [IOI2000]邮局 题解
    简要题意线段上有\(V\)个村庄,现在要建\(P\)个邮局,使每个村庄到最近的邮局的距离之和最小。50分做法设\(dp[i][j]\)表示第一个村庄到第\(i\)个村庄,建了\(j\)个......