2302: [HAOI2011]Problem c

2302: [HAOI2011]Problem c

Time Limit: 30 Sec  Memory Limit: 256 MB
Submit: 645  Solved: 352
[ Submit][ Status][ Discuss]

Description

给n个人安排座位,先给每个人一个1~n的编号,设第i个人的编号为ai(不同人的编号可以相同),接着从第一个人开始,大家依次入座,第i个人来了以后尝试坐到ai,如果ai被占据了,就尝试ai+1,ai+1也被占据了的话就尝试ai+2,……,如果一直尝试到第n个都不行,该安排方案就不合法。然而有m个人的编号已经确定(他们或许贿赂了你的上司...),你只能安排剩下的人的编号,求有多少种合法的安排方案。由于答案可能很大,只需输出其除以M后的余数即可。

 

Input

第一行一个整数T,表示数据组数

对于每组数据,第一行有三个整数,分别表示n、m、M

若m不为0,则接下来一行有m对整数,p1、q1,p2、q2 ,…, pm、qm,其中第i对整数pi、qi表示第pi个人的编号必须为qi

 

Output

对于每组数据输出一行,若是有解则输出YES,后跟一个整数表示方案数mod M,注意,YES和数之间只有一个空格,否则输出NO

 

Sample Input

2

4 3 10

1 2 2 1 3 1

10 3 8882

7 9 2 9 5 10


Sample Output



YES 4

NO



HINT



100%的数据满足:1≤T≤10,1≤n≤300,0≤m≤n,2≤M≤109,1≤pi、qi≤n   且保证pi互不相同。



 

Source

[ Submit][ Status][ Discuss]

思维太僵,,结果卡住了= =
如果方案合法,要满足任意大于等于i的数被选次数不超过n - i + 1
想到这里后面就不行了。。。。。。
反过来,任意小于等于i的数被选次数至少要i,不然前面的填不满
这样,f[i][j]:1~i的数字被选了j次的方案数,m个限制条件等于是说转移有固定增量
方程挺好写,,不说了。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<bitset>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;

const int maxn = 303;
typedef long long LL;

int T,n,m,C[maxn][maxn],f[maxn][maxn],sum[maxn];
bool vis[maxn][maxn]; LL P;

int Add(const LL &x,const LL &y) {return (x + y) % P;}
int Mul(const LL &x,const LL &y) {return x*y%P;}

int main()
{
	#ifdef DMC
		freopen("DMC.txt","r",stdin);
	#endif
	
	cin >> T;
	while (T--)
	{
		cin >> n >> m >> P; 
		int res = n - m,Sum = 0;
		memset(sum,0,sizeof(sum));
		while (m--)
		{
			int x; scanf("%d%d",&x,&x); ++sum[x];
		}
		C[0][0] = 1;
		for (int i = 1; i <= res; i++)
		{
			C[i][0] = 1;
			for (int j = 1; j <= i; j++)
				C[i][j] = Add(C[i-1][j],C[i-1][j-1]);
		}
		memset(f,0,sizeof(f)); f[0][0] = 1;
		memset(vis,0,sizeof(vis)); vis[0][0] = 1;
		for (int i = 0; i < n; i++)
		{
			Sum += sum[i];
			for (int j = i; j <= n; j++)
			{
				if (!vis[i][j]) continue;
				int rs = res - (j - Sum);
				for (int k = 0; k <= rs; k++)
				{
					int Nex = j + k + sum[i+1];
					if (Nex > n) break; vis[i+1][Nex] = 1;
					f[i+1][Nex] = Add(f[i+1][Nex],Mul(C[rs][k],f[i][j]));
				}
			}
		}
		if (!vis[n][n]) puts("NO");
		else printf("YES %d\n",f[n][n]);
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值