BZOJ2302 [HAOI2011]Problem c 【dp】
題目
給n個人安排座位,先給每個人一個1~n的編號,設第i個人的編號為ai(不同人的編號可以相同),接著從第一個人開始,大家依次入座,第i個人來了以後嘗試坐到ai,如果ai被占據了,就嘗試ai+1,ai+1也被占據了的話就嘗試ai+2,……,如果一直嘗試到第n個都不行,該安排方案就不合法。然而有m個人的編號已經確定(他們或許賄賂了你的上司...),你只能安排剩下的人的編號,求有多少種合法的安排方案。由於答案可能很大,只需輸出其除以M後的余數即可。
輸入格式
第一行一個整數T,表示數據組數
對於每組數據,第一行有三個整數,分別表示n、m、M
若m不為0,則接下來一行有m對整數,p1、q1,p2、q2 ,…, pm、qm,其中第i對整數pi、qi表示第pi個人的編號必須為qi
輸出格式
對於每組數據輸出一行,若是有解則輸出YES,後跟一個整數表示方案數mod M,註意,YES和數之間只有一個空格,否則輸出NO
輸入樣例
2
4 3 10
1 2 2 1 3 1
10 3 8882
7 9 2 9 5 10
輸出樣例
YES 4
NO
提示
100%的數據滿足:1≤T≤10,1≤n≤300,0≤m≤n,2≤M≤109,1≤pi、qi≤n 且保證pi互不相同。
題解
容易發現其實這是插入順序無關的
位置插入是否合法,只要看這個位置及其之後是否坐滿
直接難以計算一個位置之後坐了多少
但是坐到一個位置前的人的編號一定比這個位置小
如果編號為一個位置及其之前的位置的人數小於這個位置的編號,說明前面的座位一定坐不滿,那麽就代表著不合法
所以我們設\(f[i][j]\)表示編號為第\(i\)個位置及其之前的人數有\(j\)人的方案數
就可以枚舉\(i\)號位坐了多少人進行轉移了
我們記一個\(sum[i]\)數組表示固定編號\(<=i\)的人數
並且將沒有固定編號的人數記為編號\(0\)
這樣子一個位置可以坐的人數就在範圍\([num[i],sum[i]]\)以內了,其中\(num[i]\)指固定編號為\(i\)的人數
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LL long long int
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<‘ ‘; puts("");
using namespace std;
const int maxn = 305,maxm = 100005,INF = 1000000000;
inline int read(){
int out = 0,flag = 1; char c = getchar();
while (c < 48 || c > 57){if (c == ‘-‘) flag = -1; c = getchar();}
while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
return out * flag;
}
int P,n,m;
LL C[maxn][maxn],f[maxn][maxn],sum[maxn],num[maxn];
void init(){
memset(f,0,sizeof(f));
memset(sum,0,sizeof(sum));
memset(num,0,sizeof(num));
C[0][0] = 1;
for (int i = 1; i <= n; i++){
C[i][0] = C[i][i] = 1;
for (int j = 1; j <= (i >> 1); j++)
C[i][j] = C[i][i - j] = (C[i - 1][j - 1] + C[i - 1][j]) % P;
}
}
int main(){
int T = read(),flag;
while (T--){
n = read(); m = read(); P = read(); flag = true;
init(); sum[0] = n - m;
for (int i = 1; i <= m; i++) read(),num[read()]++;
for (int i = 1; i <= n; i++){
sum[i] = sum[i - 1] + num[i];
if (sum[i] < i) {flag = false; break;}
}
if (!flag){puts("NO"); continue;}
f[0][0] = 1;
for (int i = 1; i <= n; i++){
for (int j = i; j <= sum[i]; j++){
for (int k = num[i]; k <= j - i + 1; k++)
f[i][j] = (f[i][j] + f[i - 1][j - k] * C[sum[i] - num[i] - (j - k)][k - num[i]] % P) % P;
}
}
printf("YES %lld\n",f[n][n]);
}
return 0;
}
BZOJ2302 [HAOI2011]Problem c 【dp】