[SHOI2009]舞会
CXIX.[SHOI2009]舞会
之前一直在往二项式反演去想,没想到最后居然成了……
我们考虑将男生和女生全部按照高度递减排序,则对于第个男生,能与他构成特殊对的女生必定是一个前缀,设前缀长度为。显然,是单调不降的。
然后,我们考虑设表示钦定对匹配,其余随意的方案数。则作一个二项式反演即可得到恰好匹配对的方案数,再做一个前缀和就是匹配至多对的方案数。
我们考虑DP。设表示钦定前个男生匹配对特殊对的方案数,则我们要求的就是的数组。
考虑第人。因为他要么匹配一对特殊对,共有 种方案(前面的人匹配的目标当前的人也一定能匹配,所以就只剩对了),要么干脆就不是钦定的对。
于是就有且。
然后,最终得到数组,再乘上一个就是我们一开始提出的数组,因为剩下的位置没有被钦定,可以随便排。
套上高精度,时间复杂度。
代码:
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?