zoukankan      html  css  js  c++  java
  • 题解 P2159 【[SHOI2009]舞会】

    (first)

    看到数据范围应该都会多多少少有点思路了,要搞一个大概是(n^3)(dp),可是貌似最多的方案数是(200!)级别的,计算器一算发现是(6e600+),于是要套高精。所以之前的(n^3)排除,将注意力放在(n^2-n^2logn),可(logn)这种奇怪的复杂度怎么会无缘无故出现在一个(n<=200)的题目?于是果断考虑(n^2dp)

    (n^2)都知道什么意思吧,就是设置两个状态的(dp)方程

    (second)

    首先应当想到先排序,因为当身高有序后才能方便找出比某一女生矮的男生有多少,以方便进行(dp)计算

    (third)

    考虑设置(dp)方程(f_{i,j})表示当前遍历到第(i)矮的女生,我们已经匹配了(j)对,并且(j)对全是女大于男的方案数,这样的话(dp)方程可以比较快地得到

    考虑将第(i)个女生与比第(i)个女生矮而不矮于(i-1)个女生的一众男生(设人数为(p))一起加入(f_{i,j})的更新

    那么(f_{i,j})首先肯定无条件继承(f_{i-1,j})的值,而当(p>j-1)时表明我们可以再已经有(j-1)对的基础上再扩展一对女大于男的舞伴,并且扩展的这一对有(p-(j-1))种方案,那么(f_{i,j})就需要加上(f_{i-1,j-1} * (p-j+1))

    (forth)

    但是(f_{n,j})还是不能帮我们解决问题,嗯

    但已经接近尾声了,考虑设置(g_j)表示选完所有对,而其中至少有(j)对的方案数,那么显然(g_j=f_{n,j} * (n-j)!)(因为剩下(n-j)对没有要求,那么就是全排列嘛)

    得到了(g_j)数组后就很容易想到容斥得到答案了(容斥系数自己可以瞎搞搞出来):

    [ans=sum_{i=0}^{k}sum_{j=i}^{n}(-1)^{j-i}* C_j^i * g_j ]

    (fifth)

    最后一点小注,本篇题解是对该题第一篇题解的一点补充,代码实现思路几乎相同。但由于彼题解有些许瑕疵(也可能是我太蒟蒻)导致没有怎么看懂,后来仔细推敲代码并手搞式子才有了以上这些想法,担心会有和我一样的小菜鸡(应该不会吧)对于首篇的做法有迷惑,于是作此题解来造(bao)福(fu)人(she)类(hui)

    (sixth)

    代码(代码中的(f_{i,j})是用的滚动数组,没算(g_j)而是直接代入(ans)公式,若还有不懂欢迎私信):

    #include<bits/stdc++.h>
    using namespace std;
    
    #define ll long long
    
    inline int read()
    {
    	int x=0,f=1;char ch=getchar();
    	for(;ch<'0'||ch>'9';ch=getchar()) f=ch=='-'?-1:1;
    	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch^48);
    	return x*f;
    }
    
    const int xx=2e2+10,xd=100;
    const int digit=1e9;
    
    int m[xx],wm[xx],n,k;
    
    struct Big
    {
    	ll a[xd];
    	inline void print()
    	{
    		for(register int i=a[0];i;--i)
    		{
    			if(i!=a[0])
    				for(register int j=1e8;j>=10;j/=10)
    					a[i]<j?putchar('0'):j=1;
    			printf("%d",a[i]);
    		}
    		puts(""); 
    	}
    }zero,one,fac[xx],f[2][xx],C[xx][xx];
    Big ans;
    Big operator * (Big x,Big y)
    {
    	Big tmp=zero;tmp.a[0]=x.a[0]+y.a[0];
    	for(register int i=1;i<=x.a[0];++i)
    		for(register int j=1;j<=y.a[0];++j)
    		{
    			tmp.a[i+j-1]+=x.a[i]*y.a[j];
    			if(tmp.a[i+j-1]>=digit)
    			{
    				tmp.a[i+j]+=tmp.a[i+j-1]/digit;
    				tmp.a[i+j-1]%=digit;
    			}
    		}
    	while(!tmp.a[tmp.a[0]]&&tmp.a[0]>1)
    		--tmp.a[0];
    	return tmp;
    }
    Big operator * (int x,Big y)
    {
    	Big tmp=zero;tmp.a[0]=y.a[0]+1;
    	for(register int i=1;i<=y.a[0];++i)
    	{
    		tmp.a[i]+=x*y.a[i];
    		if(tmp.a[i]>=digit)
    		{
    			tmp.a[i+1]+=tmp.a[i]/digit;
    			tmp.a[i]%=digit;
    		}
    	}
    	while(!tmp.a[tmp.a[0]]&&tmp.a[0]>1)
    		--tmp.a[0];
    	return tmp;
    }
    Big operator + (Big x,Big y)
    {
    	Big tmp=zero;tmp.a[0]=max(x.a[0],y.a[0])+1;
    	for(register int i=1;i<=tmp.a[0]-1;++i)
    	{
    		tmp.a[i]+=(x.a[i]+y.a[i]);
    		if(tmp.a[i]>=digit)
    		{
    			tmp.a[i+1]+=tmp.a[i]/digit;
    			tmp.a[i]%=digit;
    		}
    	}
    	while(!tmp.a[tmp.a[0]]&&tmp.a[0]>1)
    		--tmp.a[0];
    	return tmp;
    }
    Big operator - (Big x,Big y)
    {
    	Big tmp=zero;tmp.a[0]=x.a[0];
    	for(register int i=1;i<=x.a[0];++i)
    	{
    		tmp.a[i]=x.a[i]-y.a[i];
    		if(tmp.a[i]<0)
    		{
    			tmp.a[i]+=digit;
    			--x.a[i+1];
    		}
    	}
    	while(!tmp.a[tmp.a[0]]&&tmp.a[0]>1)
    		--tmp.a[0];
    	return tmp;
    }
    
    inline void pre()
    {
    	memset(zero.a,0,sizeof(zero.a));
    	zero.a[0]=one.a[0]=one.a[1]=1;
    	for(register int i=0;i<=n;++i)
    		C[i][0]=one;
    	for(register int i=1;i<=n;++i)
    		for(register int j=1;j<=n;++j)
    			C[i][j]=C[i-1][j]+C[i-1][j-1];
    	for(register int i=0;i<=1;++i)
    		for(register int j=1;j<=n;++j)
    			f[i][j]=zero;
    	fac[0]=one;
    	for(register int i=1;i<=n;++i)
    		fac[i]=i*fac[i-1];
    	f[0][0]=f[1][0]=one;
    }
    
    int main()
    {
    	n=read(),k=read(),pre();
    	for(register int i=1;i<=n;++i) m[i]=read();
    	for(register int i=1;i<=n;++i) wm[i]=read();
    	sort(m+1,m+n+1),sort(wm+1,wm+n+1);
    	for(register int i=1,K=0;i<=n;++i)
    	{
    		while(K<n&&m[K+1]<wm[i])
    			++K;
    		for(register int j=1;j<=i;++j)
    		{
    			if(K>j-1)
    				f[i&1][j]=(K-j+1)*f[(i-1)&1][j-1];
    			f[i&1][j]=f[i&1][j]+f[(i-1)&1][j];
    		}
    	}
    	ans=zero;
    	for(register int i=0;i<=k;++i)
    		for(register int j=i;j<=n;j+=2)
    			ans=ans+(f[n&1][j]*fac[n-j]*C[j][i]);
    	for(register int i=0;i<=k;++i)
    		for(register int j=i+1;j<=n;j+=2)
    			ans=ans-(f[n&1][j]*fac[n-j]*C[j][i]);
    	ans.print();
    	return 0;
    }
    
  • 相关阅读:
    MySQL DNS反查导致连接缓慢
    重写官方TodoList,对于初学react+redux的人来说,很有好处
    第一个博客试一试
    需求分析
    原来==的优先级比&高 (转自地址为http://blog.163.com/cynicly@126/blog/static/1206105820103893715302/的网易博客)
    E: 无法获得锁 /var/lib/apt/lists/lock open (11 Resource temporarily unavailable)
    我的博客园终于开通啦!
    (转自CSDN日新为道的专栏)printf的格式控制符,在CSDN看到的,转在这里
    qrc_image.cpp未找到
    QT打开保存的html文件无法显示图片
  • 原文地址:https://www.cnblogs.com/ALANALLEN21LOVE28/p/12635821.html
Copyright © 2011-2022 走看看