1. 程式人生 > >BZOJ2302 [HAOI2011]Problem c 【dp】

BZOJ2302 [HAOI2011]Problem c 【dp】

red 多少 void ons sum ems () \n 直接

題目

給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】