zoukankan      html  css  js  c++  java
  • [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;
    }
    

  • 相关阅读:
    Linux内核中锁机制之RCU、大内核锁
    Linux内核中锁机制之完成量、互斥量
    Linux内核中锁机制之信号量、读写信号量
    Linux内核中锁机制之内存屏障、读写自旋锁及顺序锁
    Linux内核中锁机制之原子操作、自旋锁
    Linux内核jiffies简介
    pdflush机制
    ext2文件系统
    从ext2文件系统上读出超级块
    ext2磁盘布局
  • 原文地址:https://www.cnblogs.com/Troverld/p/14601366.html
Copyright © 2011-2022 走看看