[SHOI2009]舞会

CXIX.[SHOI2009]舞会

之前一直在往二项式反演去想,没想到最后居然成了……

我们考虑将男生和女生全部按照高度递减排序,则对于第\(i\)个男生,能与他构成特殊对的女生必定是一个前缀,设前缀长度为\(num_i\)。显然,\(num_i\)是单调不降的。

然后,我们考虑设\(f_i\)表示钦定\(i\)对匹配,其余随意的方案数。则\(f_i\)作一个二项式反演即可得到恰好匹配\(i\)对的方案数,再做一个前缀和就是匹配至多\(i\)对的方案数。

我们考虑DP。设\(f_{i,j}\)表示钦定前\(i\)个男生匹配\(j\)对特殊对的方案数,则我们要求的就是\(f_n\)的数组。

考虑第\(i\)人。因为他要么匹配一对特殊对,共有 \(num_i-j\) 种方案(前面的人匹配的目标当前的人也一定能匹配,所以就只剩\(num_i-j\)对了),要么干脆就不是钦定的对。

于是就有\(f_{i-1,j}\times(num_i-j)\rightarrow f_{i,j+1}\)\(f_{i-1,j}\rightarrow f_{i,j}\)

然后,最终得到\(f_{n,i}\)数组,再乘上一个\((n-i)!\)就是我们一开始提出的\(f\)数组,因为剩下的位置没有被钦定,可以随便排。

套上高精度,时间复杂度\(O(n^2\times\text{高精度复杂度})\)

代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,a[310],b[310],num[310];
struct Wint:vector<int>{
    Wint(){clear();}
    Wint(int x){clear();while(x)push_back(x%10),x/=10;}
    void operator ~(){
        for(int i=0;i+1<size();i++){
        	(*this)[i+1]+=(*this)[i]/10,(*this)[i]%=10;
        	while((*this)[i]<0)(*this)[i]+=10,(*this)[i+1]--;
		}
        while(!empty()&&back()>9){
            int tmp=back()/10;
            back()%=10;
            push_back(tmp);
        }
        while(!empty()&&!back())pop_back();
    }
    void operator +=(const Wint &x){
        if(size()<x.size())resize(x.size());
        for(int i=0;i<x.size();i++)(*this)[i]+=x[i];
        ~*this;
    }    
	void operator -=(const Wint &x){
        for(int i=0;i<x.size();i++)(*this)[i]-=x[i];
        ~*this;
    }
    friend Wint operator +(Wint x,const Wint &y){
    	x+=y;
    	return x;
	}
    friend Wint operator *(const Wint &x,const Wint &y){
        Wint z;
        if(!x.size()||!y.size())return z;
        z.resize(x.size()+y.size()-1);
        for(int i=0;i<x.size();i++)for(int j=0;j<y.size();j++)z[i+j]+=x[i]*y[j];
        ~z;
        return z;
    }
    void print()const{
        if(empty()){putchar('0');return;}
        for(int i=size()-1;i>=0;i--)putchar('0'+(*this)[i]);
    }
}C[310][310],f[310][310],fac[310];
signed main(){
	scanf("%d%d",&n,&m);
	fac[0]=1;
	for(int i=1;i<=n;i++)fac[i]=fac[i-1]*i;
	for(int i=0;i<=n;i++)C[i][0]=1;
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)C[i][j]=C[i-1][j-1]+C[i-1][j];
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	sort(a+1,a+n+1),reverse(a+1,a+n+1);
	for(int i=1;i<=n;i++)scanf("%d",&b[i]);
	sort(b+1,b+n+1),reverse(b+1,b+n+1);
	for(int i=1,j=1;i<=n;i++){
		while(j<=n&&b[j]>a[i])j++;
		num[i]=j-1;
	}
	f[0][0]=1;
	for(int i=0;i<n;i++)for(int j=0;j<=i;j++){
		if(j<=num[i+1])f[i+1][j+1]+=f[i][j]*(num[i+1]-j);
		f[i+1][j]+=f[i][j];
	}
	for(int i=0;i<=n;i++)f[n][i]=f[n][i]*fac[n-i];
	for(int i=n;i>=0;i--)for(int j=i+1;j<=n;j++)f[n][i]-=C[j][i]*f[n][j];
//	for(int i=0;i<=n;i++)printf("%d ",f[n][i]);puts("");
	for(int i=1;i<=n;i++)f[n][i]+=f[n][i-1];
	f[n][m].print();
	return 0;
}

原文地址:https://www.cnblogs.com/Troverld/p/14601366.html