【MIPT Workshop Open 1 K】Blocks 题解

题目大意

  有 n n n 个柱子,高度构成 1 1 1~ n n n 的排列。现在你要把他们排在一行,使得从左边看能看到恰好 l l l 根柱子,从右边看能看到恰好 p p p 根柱子。求方案数。共 m m m 组数据。
   n ≤ 50000 ,   l , p ≤ 100 ,   m ≤ 1 0 5 n \leq 50000,~l,p \leq 100,~m \leq 10^5 n50000, l,p100, m105

  \\~  
  \\~  
  \\~  

题解

  可以很自然地想到 dp,从大到小放柱子,那么放在边上的就可以被看到,放到中间的就看不到。

  这里要注意的是不要把左边和右边分开考虑,就是说不要什么分别算左边和右边然后合并,或者 dp 式子里设左边和右边分别看到了多少柱子。我不会告诉你我就是这样被卡了 2h 多。这样子始终会有一个 O ( l ∗ p ) O(l*p) O(lp) 的时间。

  但是可以发现一个新的柱子放两边其实是本质相同的!
  所以设 f i , j f_{i,j} fi,j 表示从大到小放了 i i i 个柱子(方便起见,去掉最高的那个),两边可见的共有 j j j 个,的方案数。新加进来一个柱子要么放两边使 j j j 加一,要么放中间。
  最后询问的时候乘个 C p + q − 2 p − 1 C_{p+q-2}^{p-1} Cp+q2p1 就好啦。

  这样就是 O ( n p + m ) O(np+m) O(np+m) 的了。

代码

#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;

typedef long long LL;

const int maxn=5e4+5, maxp=205;
const LL mo=1e9+7;

int n,p,q;
LL f[maxn][maxp],C[maxp][maxp];

int m;
int main()
{
	n=50000, p=200;
	f[0][0]=1;
	fo(i,1,n-1)
		fo(j,1,min(p,i)) f[i][j]=(f[i-1][j-1]+f[i-1][j]*(i-1))%mo;
	fo(i,0,p)
	{
		C[i][0]=1;
		fo(j,1,i) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mo;
	}
	
	scanf("%d",&m);
	while (m--)
	{
		scanf("%d %d %d",&n,&p,&q);
		
		LL ans=f[n-1][p+q-2]*C[p+q-2][p-1]%mo;
		
		printf("%lld\n",ans);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值