一道(我以为是推柿子的)计数dp 牛客寒假集训 阿宁睡大觉

先来看看它的祖宗

cf 559C

大意:

给定h ∗ w的矩阵,上面有n个黑点,其余的都是白点。每次只能向下走或向右走,问从(1,1) 走到 (h,w) 不经过黑点的路径有多少条,答案对1e9+7取模。n<=2000,h,w<=2e5

思路:给的矩阵有点大,所以我们没办法按常规思路n^2解决。又注意到黑点的个数其实不多,所以我们不妨以其为对象进行dp。显然的,把点按横坐标排序,我们设dp[i]表示走到第i个黑点且中间没有经过其它黑点的方案数,那么这样就能支撑n^2的遍历了。

不考虑黑点的话,(1,1)->(i,j)的方案数是C(i+j-2,i-1),那么只要中间做一下容斥就好了

对于点j<i,j的横坐标不大于x的横坐标,如果j的纵坐标大于x的纵坐标,显然是没有从j走到i的方案的,所以这里我们不用容斥。否则减去dp[j]*(j->i的方案数)(因为每一个点的dp值都是代表其第一次被走到的方案数,不会包括其它黑点,所以这样是不会多减的),j->i的方案数用上述做法即得。

这样就能做完这题了。

dp就是这样,看看思路好像也就这么一回事,可我就是想不到。。。(泪

code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const ll N=2e5+10;
const ll mod=1e9+7;
ll ksm(ll x,ll y)
{
	ll ans=1;
	while(y)
	{
		if(y&1) ans=ans*x%mod;
		x=x*x%mod;
		y>>=1;
	}
	return ans;
}
ll inv(ll x)
{
	return ksm(x,mod-2);
}
ll p[N],pp[N];
void init()
{
	p[0]=1;
	for(ll i=1;i<=200000;++i) p[i]=p[i-1]*i%mod;
	pp[200000]=inv(p[200000]);
	for(ll i=200000-1;i>=0;--i) pp[i]=pp[i+1]*(i+1)%mod;
}
ll n,m,h,w;
struct ty
{
	ll x,y;
}mas[2010];
bool cmp(ty a,ty b)
{
	if(a.x==b.x) return a.y<b.y;
	return a.x<b.x; 
}
ll C(ll n,ll m)
{
	if(n<m) return 0;
	return p[n]*pp[m]%mod*pp[n-m]%mod;
}
ll dp[2010];
void solve()
{
	init();
	cin>>h>>w>>m;
	for(int i=1;i<=m;++i)
	{
		cin>>mas[i].x>>mas[i].y;
	}
	mas[m+1].x=h,mas[m+1].y=w;
	sort(mas+1,mas+1+m,cmp);
	for(int i=1;i<=m+1;++i)
	{
		dp[i]=C(mas[i].x-1+mas[i].y-1,mas[i].x-1);
		for(int j=1;j<i;++j)
		{
			if(mas[j].y>mas[i].y) continue;
			dp[i]=((dp[i]-dp[j]*C(mas[i].x-mas[j].x+mas[i].y-mas[j].y,mas[i].x-mas[j].x)%mod)%mod+mod)%mod;
		}
	}
//	for(int i=1;i<=m+1;++i) cout<<dp[i]<<' ';
//	cout<<endl; 
	cout<<dp[m+1]<<endl;
}
int main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	solve();
	return 0;
}

然后就是牛客那道了

阿宁睡大觉

大意:

算是上一题的弱化版本,黑点数很少,所以其实也可以状压去做,然后就遍历一下好梦点,加一下方案数就可以了。

code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const ll N=2e5+10;
const ll mod=1e9+7;
ll ksm(ll x,ll y)
{
	ll ans=1;
	while(y)
	{
		if(y&1) ans=ans*x%mod;
		x=x*x%mod;
		y>>=1;
	}
	return ans;
}
ll inv(ll x)
{
	return ksm(x,mod-2);
}
ll p[N],pp[N];
void init()
{
	p[0]=1;
	for(ll i=1;i<=200000;++i) p[i]=p[i-1]*i%mod;
	pp[200000]=inv(p[200000]);
	for(ll i=200000-1;i>=0;--i) pp[i]=pp[i+1]*(i+1)%mod;
}
ll n,m,h,w;
struct ty
{
	ll x,y;
}mas[2010];
bool cmp(ty a,ty b)
{
	if(a.x==b.x) return a.y<b.y;
	return a.x<b.x; 
}
ll C(ll n,ll m)
{
	if(n<m) return 0;
	return p[n]*pp[m]%mod*pp[n-m]%mod;
}
ll dp[2010];
void solve()
{
	init();
	cin>>n>>m;
	for(int i=1;i<=m;++i)
	{
		cin>>mas[i].x>>mas[i].y;
	}
	sort(mas+1,mas+1+m,cmp);
	for(int i=1;i<=m+1;++i)
	{
		dp[i]=C(mas[i].x-1+mas[i].y-1,mas[i].x-1);
		for(int j=1;j<i;++j)
		{
			if(mas[j].y>mas[i].y) continue;
			dp[i]=((dp[i]-dp[j]*C(mas[i].x-mas[j].x+mas[i].y-mas[j].y,mas[i].x-mas[j].x)%mod)%mod+mod)%mod;
		}
	}
	ll sum=0;
	for(int i=1;i<=n;++i)
	{
		ll x=i;ll y=n-i+1;
		ll gt=C(x+y-2,x-1);
		for(int j=1;j<=m;++j)
		{
			if(mas[j].x>x||mas[j].y>y) continue;
			gt=((gt-dp[j]*C(x-mas[j].x+y-mas[j].y,x-mas[j].x)%mod)%mod+mod)%mod;
		}
		sum=(sum+gt)%mod;
	}
	cout<<sum<<endl;
}
int main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	solve();
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值