#include <bits/stdc++.h> using namespace std; const int N = 100010; int n, k; int f[N], a[N]; int main() { scanf("%d", &n); for(int i = 1; i <= n; ++i) scanf("%d", &a[i]); for(int i = 2; i <= n; ++i) { f[i] = 0x3f3f3f3f; if(i >= 2) f[i] = min(f[i], f[i - 1] + abs(a[i] - a[i - 1])); if(i >= 3) f[i] = min(f[i], f[i - 2] + abs(a[i] - a[i - 2])); } printf("%d\n", f[n]); }
#include <bits/stdc++.h> using namespace std; const int N = 100010; int n, k; int f[N], a[N]; int main() { scanf("%d%d", &n, &k); for(int i = 1; i <= n; ++i) scanf("%d", &a[i]); for(int i = 2; i <= n; ++i) { f[i] = 0x3f3f3f3f; for(int j = max(i - k, 1); j < i; ++j) { f[i] = min(f[i], f[j] + abs(a[i] - a[j])); } } printf("%d\n", f[n]); }
#include <bits/stdc++.h> using namespace std; const int N = 100010; int n, k; int f[N][3], a[N][3]; int main() { scanf("%d", &n); for(int i = 1; i <= n; ++i) scanf("%d%d%d", &a[i][0], &a[i][1], &a[i][2]); for(int i = 1; i <= n; ++i) { for(int j = 0; j < 3; ++j) { for(int k = 0; k < 3; ++k) { if(j == k) continue; f[i][j] = max(f[i][j], f[i - 1][k] + a[i][j]); } } } printf("%d\n", max(max(f[n][0], f[n][1]), f[n][2])); }
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 100010; int n, W; ll f[N], w[N], v[N]; int main() { scanf("%d%d", &n, &W); ll ans = 0; for(int i = 1; i <= n; ++i) scanf("%lld%lld", &w[i], &v[i]); for(int i = 1; i <= n; ++i) { for(int j = W; j >= w[i]; --j) { f[j] = max(f[j], f[j - w[i]] + v[i]); } } for(int i = 1; i <= W; ++i) ans = max(ans, f[i]); printf("%lld\n", ans); }
對比前面四題不那麼傻的一題
很妙的一個轉化,注意到這題的\(W\)很大,可是\(v\)很小,因此不妨轉換一下狀態,設\(f[i,j]\)表示前\(i\)個物品,選出後的總價值爲\(j\),須要的最小體積。那麼有\(f[i][j]=\min\{f[i-1][j-v[i]]+w[i]\}\),答案就是對於全部\(f[n][j]\le W\)的\(j\)取\(\max\)。c++
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 100010; int n, v[N], W, w[N]; int f[110][N]; int main() { scanf("%d%d", &n, &W); int sum = 0; for(int i = 1; i <= n; ++i) scanf("%d%d", &w[i], &v[i]), sum += v[i]; memset(f, 0x3f, sizeof(f)); f[0][0] = 0; int ans = 0; for(int i = 1; i <= n; ++i) { for(int j = 0; j <= sum; ++j) { if(j >= v[i]) f[i][j] = min(f[i][j], f[i - 1][j - v[i]] + w[i]); f[i][j] = min(f[i][j], f[i - 1][j]); if(f[i][j] <= W) ans = max(ans, j); } } printf("%d\n", ans); }
LCS作一下就好,輸出方案的話從\([n,m]\)往回找就行了,用個棧壓一下答案。函數
#include <bits/stdc++.h> using namespace std; const int N = 3010; char s[N], t[N], st[N]; int f[N][N]; int main() { scanf("%s%s", s + 1, t + 1); int n = strlen(s + 1), m = strlen(t + 1); for(int i = 1; i <= n; ++i) { for(int j = 1; j <= m; ++j) { f[i][j] = max(f[i - 1][j], f[i][j - 1]); if(s[i] == t[j]) f[i][j] = max(f[i][j], f[i - 1][j - 1] + 1); } } int now = f[n][m] + 1; int top = 0; for(int i = n; i; --i) { for(int j = m; j; --j) { if(s[i] == t[j] && f[i][j] == now - 1) { --now; st[++top] = s[i]; break; } } } while(top) putchar(st[top--]); }
#include <bits/stdc++.h> using namespace std; const int N = 100010; int n, m, deg[N], q[N], f[N]; int cnt, head[N]; struct edge { int to, nxt; } e[N]; void ins(int u, int v) { e[++cnt] = (edge) { v, head[u] }; head[u] = cnt; } int main() { scanf("%d%d", &n, &m); for(int i = 1, u, v; i <= m; ++i) { scanf("%d%d", &u, &v); ins(u, v); deg[v]++; } int l = 1, r = 1; for(int i = 1; i <= n; ++i) { if(!deg[i]) q[r++] = i; } while(l < r) { int u = q[l++]; for(int i = head[u]; i; i = e[i].nxt) { int v = e[i].to; f[v] = max(f[v], f[u] + 1); deg[v]--; if(!deg[v]) q[r++] = v; } } int ans = 0; for(int i = 1; i <= n ;++i) ans = max(ans, f[i]); printf("%d\n", ans); return 0; }
#include <bits/stdc++.h> using namespace std; const int N = 1010; const int mod = 1e9 + 7; int h, w, f[N][N]; char a[N][N]; int main() { scanf("%d%d", &h, &w); f[1][1] = 1; for(int i = 1; i <= h; ++i) scanf("%s", a[i] + 1); for(int i = 1; i <= h; ++i) { for(int j = 1; j <= w; ++j) { if(i == 1 && j == 1) continue; if(a[i][j] == '.' && a[i - 1][j] == '.') (f[i][j] += f[i - 1][j]) %= mod; if(a[i][j] == '.' && a[i][j - 1] == '.') (f[i][j] += f[i][j - 1]) %= mod; } } printf("%d\n", f[h][w]); }
#include <bits/stdc++.h> using namespace std; const int N = 3000; int n; double p[N], f[2][N]; int main() { scanf("%d", &n); int cur = 0; for(int i = 1; i <= n; ++i) scanf("%lf", &p[i]); f[1][0] = 1; for(int i = 1; i <= n; ++i) { memset(f[cur], 0, sizeof(f[cur])); for(int j = 0; j <= i; ++j) { f[cur][j] += f[cur ^ 1][j] * (1 - p[i]); f[cur][j] += f[cur ^ 1][j - 1] * p[i]; } cur ^= 1; } double ans = 0; for(int i = n / 2 + 1; i <= n; ++i) ans += f[cur ^ 1][i]; printf("%.15lf\n", ans); }
設\(f[i][j][k]\)表示當前\(a_x=3\)的數有\(i\)個,\(=2\)的有\(j\)個,\(=1\)的有\(k\)個。
則有方程
\[ f[i][j][k]=\frac {\left( 1+f[i-1][j+1][k]*\frac i n+f[i][j-1][k+1] * \frac j n + f[i][j][k-1]* \frac k n \right)}{\frac {i+j+k} n} \]
答案即爲\(f[cnt[3]][cnt[2]][cnt[1]]\)優化
#include <bits/stdc++.h> using namespace std; const int N = 310; double f[N][N][N]; int n, a[N], cnt[5]; int main() { scanf("%d", &n); for(int i = 1; i <= n; ++i) scanf("%d", &a[i]), cnt[a[i]]++; for(int i = 0; i <= n; ++i) { for(int j = 0; j <= n; ++j) { for(int k = 0; k <= n; ++k) { if(i + j + k > n) continue; if(!i && !j && !k) continue; if(i > cnt[3]) continue; if(j > cnt[3] + cnt[2]) continue; if(k > cnt[3] + cnt[2] + cnt[1]) continue; double p1 = 1.0 * i / n, p2 = 1.0 * j / n, p3 = 1.0 * k / n; double p4 = 1.0 * (i + j + k) / n; f[i][j][k] = (double)(1+(i?f[i-1][j+1][k]:0)*p1+(j?f[i][j-1][k+1]:0)*p2+(k?f[i][j][k-1]:0)*p3)/p4; } } } printf("%.10lf\n", f[cnt[3]][cnt[2]][cnt[1]]); }
\(SG\)函數板子題,根據\(SG\)定理,只須要\(sg(k)\)不爲\(0\)就先手必勝。
對於\(\text{mex}\)運算我直接從第一個數開始枚舉了...須要複雜度正確的話就須要寫個主席樹或者寫個權值分塊。複雜度是\(O(nk\log A)\)或者\(O(nk \sqrt A)\),若是直接枚舉最壞是\(O(nkA)\)的可是跑不到,能過。ui
#include <bits/stdc++.h> using namespace std; const int N = 100010; int sg[N], n, k, a[N], vis[N]; int mex(int x) { memset(vis, 0, sizeof(vis)); for(int i = 1; i <= n; ++i) { if(x - a[i] >= 0) vis[sg[x - a[i]]] = 1; } for(int i = 0; i <= 100000; ++i) if(!vis[i]) return i; } int main() { scanf("%d%d", &n, &k); for(int i = 1; i <= n; ++i) { scanf("%d", &a[i]); } for(int j = 1; j <= k; ++j) sg[j] = mex(j); if(sg[k]) puts("First"); else puts("Second"); }
設\(f[l][r]\)表示按策略取完區間\([l,r]\)能夠獲得的分數。
直接按他們的策略模擬便可,這個策略是有階段性的。spa
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 3100; ll f[N][N]; int n, a[N]; int main() { scanf("%d", &n); for(int i = 1; i <= n; ++i) scanf("%d", &a[i]); for(int i = 1; i <= n; ++i) if(n & 1) f[i][i] = a[i]; else f[i][i] = -a[i]; for(int len = 2; len <= n; ++len) { for(int l = 1; l <= n; ++l) { int r = l + len - 1; if((n - len) & 1) f[l][r] = min(f[l + 1][r] - a[l], f[l][r - 1] - a[r]); else f[l][r] = max(f[l + 1][r] + a[l], f[l][r - 1] + a[r]); } } printf("%lld\n", f[1][n]); }
用前綴和優化一下轉移便可。設計
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 100010; const ll mod = 1e9 + 7; int n, k, a[N]; ll f[101][N], sum[101][N]; /* f[i][j]表示前i我的,一共分了j份。 */ int main() { int cur = 1; scanf("%d%d", &n, &k); for(int i = 1; i <= n; ++i) scanf("%d", &a[i]); sum[0][1] = f[0][1] = 1; for(int i = 1; i <= k + 1; ++i) sum[0][i] = 1; for(int i = 1; i <= n; ++i) { for(int j = 1; j <= k + 1; ++j) { (f[i][j] = sum[i - 1][j] - sum[i - 1][max(j - a[i] - 1, 0)]) %= mod; sum[i][j] = (f[i][j] + sum[i][j - 1]) % mod; } } printf("%lld\n", (f[n][k + 1] % mod + mod) % mod); /* for(int i = 1; i <= n; ++i) { for(int j = 1; j <= k + 1; ++j) printf("%d ", sum[i][j]); puts(""); }*/ }
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 500; ll f[N][N], sum[N]; int n, a[N], ans = 0; int main() { scanf("%d", &n); for(int i = 1; i <= n; ++i) scanf("%d", &a[i]), sum[i] = sum[i - 1] + a[i]; memset(f, 0x3f, sizeof(f)); for(int i = 1; i <= n; ++i) f[i][i] = 0; for(int len = 2; len <= n; ++len) { for(int l = 1; l <= n; ++l) { int r = l + len - 1; if(r > n) break; for(int k = l; k < r; ++k) { f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r] + sum[r] - sum[l - 1]); } } } printf("%lld\n", f[1][n]); }
狀壓dp。
一開始寫了很傻的\(O(n^2 2^n)\)竟然都過了,\(O(n 2 ^n)\)的作法就是先枚舉集合,而後統計出當前匹配了多少人\(i\),對\(f[i][S]\)枚舉轉移點轉移便可。code
#include <bits/stdc++.h> using namespace std; const int N = 22; const int mod = 1e9 + 7; int n, a[N][N]; int f[N][1 << N]; int main() { scanf("%d", &n); for(int i = 1; i <= n; ++i) { for(int j = 1; j <= n; ++j) scanf("%d", &a[i][j]); } f[0][0] = 1; for(int S = 1; S < (1 << n); ++S) { int i = 0; for(int j = 0; j < n; ++j) if((S >> j) & 1) ++i; for(int j = 0; j < n; ++j) { if(((S >> j) & 1) && a[i][j + 1]) (f[i][S] += f[i - 1][S ^ (1 << j)]) %= mod; } } printf("%d\n", f[n][(1 << n) - 1]); }
設\(f[i][0/1]\)表示節點\(i\)塗成黑色/白色的方案數。
有方程\(f[i][1]=\prod_{j\in son(i)} (f[j][0]+f[j][1]),f[i][0]=\sum_{j\in son(i)} f[j][1]\)排序
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll mod = 1e9 + 7; const int N = 100010; int n; ll f[N][2]; // f[i][0/1]表示將該節點塗成黑色/白色的方案數 int cnt, head[N]; struct edge { int to, nxt; } e[N << 1]; void ins(int u, int v) { e[++cnt] = (edge) { v, head[u] }; head[u] = cnt; } void dfs(int u, int fa) { f[u][0] = f[u][1] = 1; for(int i = head[u]; i; i = e[i].nxt) { int v = e[i].to; if(v == fa) continue; dfs(v, u); (f[u][0] *= f[v][1]) %= mod; (f[u][1] *= (f[v][0] + f[v][1]) % mod) %= mod; } } int main() { scanf("%d", &n); for(int u, v, i = 1; i < n; ++i) { scanf("%d%d", &u, &v); ins(u, v), ins(v, u); } dfs(1, 0); printf("%lld\n", (f[1][0] + f[1][1]) % mod); }
用BIT維護一下前綴\(\max\)轉移便可。it
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 200010; int n, a[N], h[N]; ll c[N]; #define lowbit(i) (i & -i) void add(int x, ll v) { for(int i = x; i <= n; i += lowbit(i)) c[i] = max(c[i], v); } ll query(int x) { ll ans = 0; for(int i = x; i; i -= lowbit(i)) ans = max(ans, c[i]); return ans; } int main() { scanf("%d", &n); for(int i = 1; i <= n; ++i) scanf("%d", &h[i]); for(int i = 1; i <= n; ++i) scanf("%d", &a[i]); for(int i = 1; i <= n; ++i) { add(h[i], query(h[i]) + a[i]); } printf("%lld\n", query(n)); }
和bzoj cow relays同樣。
把矩陣乘法寫成相似floyd那樣,用矩陣快速冪維護便可。class
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 60; const ll mod = 1e9 + 7; int n; ll K; struct mat { ll d[N][N]; mat() {memset(d, 0, sizeof(d));} mat operator * (mat &x) { mat ans; for(int k = 1; k <= n; ++k) { for(int i = 1; i <= n; ++i) { for(int j = 1; j <= n; ++j) { (ans.d[i][j] += d[i][k] * x.d[k][j] % mod) %= mod; } } } return ans; } } A[65]; int main() { scanf("%d%lld", &n, &K); for(int i = 1; i <= n; ++i) for(int j = 1; j <= n; ++j) scanf("%lld", &A[0].d[i][j]); for(ll i = 1; (1LL << i) <= K; ++i) { A[i] = A[i - 1] * A[i - 1]; } mat ans; for(int i = 1; i <= n; ++i) ans.d[i][i] = 1; for(ll i = 0; (1LL << i) <= K; ++i) if((K >> i) & 1LL) ans = ans * A[i]; ll sum = 0; for(int i = 1; i <= n; ++i) { for(int j = 1; j <= n; ++j) (sum += ans.d[i][j]) %= mod; } printf("%lld\n", sum); }
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 100010; const int mod = 1e9 + 7; char s[N]; int d, n, a[N]; int f[10010][110]; int dfs(int cnt, int limit, int lead, int s) { if(!cnt) return !s && !lead; if(~f[cnt][s] && !limit && !lead) return f[cnt][s]; int sum = 0, ed = limit ? a[cnt] : 9; for(int i = 0; i <= ed; ++i) { (sum += dfs(cnt - 1, limit && (i == ed), lead && (!i), (s + i) % d)) %= mod; } if(!limit && !lead) return f[cnt][s] = sum; return sum; } int main() { memset(f, -1, sizeof(f)); scanf("%s%d", s + 1, &d); n = strlen(s + 1); for(int i = 1; i <= n; ++i) a[i] = s[i] - '0'; reverse(a + 1, a + n + 1); printf("%lld\n", dfs(n, 1, 1, 0)); }
設\(f[i][j]\)表示填在第\(i\)位的數在已填入的數中排名爲\(j\)。
考慮這樣子設計狀態爲何是對的,若是第二維表示的是填入的數是\(j\),那麼並不能知道前面\(i-1\)個數具體填的是什麼(由於排列不可重,因此不能填重複的數),也就無從轉移。
設成在已填入數中排名爲\(j\),這樣知道的是相對關係,因此可轉移點也就肯定了(相對小於它的點)。
對於\(s[i]='<'\),有\(f[i][j]=\sum_{k< j} f[i-1][k]\)
對於\(s[i]='>'\),有\(f[i][j]=\sum_{j\le k< i}f[i-1][k]\)
維護一下前綴和便可\(O(n^2)\)轉移。
設計狀態的時候設計成相對關係有時候頗有用。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 3010; const int mod = 1e9 + 7; char s[N]; int f[N][N], sum[N], n; /* f[i][j]表示位置i,而後位置i的數在已填入的數中排名爲j '>' f[i][j] = \sum_{k < j} f[i - 1][k] '<' f[i][j] = \sum_{k >= j} f[i - 1][k] */ int main() { scanf("%d", &n); scanf("%s", s + 2); f[1][1] = sum[1] = 1; for(int i = 2; i <= n; ++i) { for(int j = 1; j <= i; ++j) { if(s[i] == '<') (f[i][j] += sum[j - 1]) %= mod; else (f[i][j] += sum[i - 1] - sum[j - 1] + mod) %= mod; } for(int j = 1; j <= i; ++j) sum[j] = (sum[j - 1] + f[i][j]) % mod; } printf("%d\n", sum[n]); }
狀壓dp。枚舉子集轉移便可。
\(f[S]=\sum f[S_1]+val[S\oplus S_1]\)其中\(S_1\)爲\(S\)的子集。
\(val[S]\)能夠\(O(n^22^n)\)處理出來。
複雜度爲\(O(3^n+n^22^n)\)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 17; int n, a[N][N], b[N], cnt; ll f[1 << N], val[1 << N]; int main() { scanf("%d", &n); for(int i = 0; i < n; ++i) { for(int j = 0; j < n; ++j) scanf("%d", &a[i][j]); } for(int S = 1; S < (1 << n); ++S) { cnt = 0; for(int i = 0; i < n; ++i) { if((S >> i) & 1) b[++cnt] = i; } for(int i = 1; i <= cnt; ++i) { for(int j = i + 1; j <= cnt; ++j) val[S] += a[b[i]][b[j]]; } } for(int S = 1; S < (1 << n); ++S) { for(int S1 = S; S1; S1 = (S1 - 1) & S) { f[S] = max(f[S], f[S ^ S1] + val[S1]); } } printf("%lld\n", f[(1 << n) - 1]); }
線段樹優化dp。
由於若是選了一個點,那麼右端點在它以前的線段顯然不會對這個點有影響,那麼把那些線段的貢獻都加上去以後取max轉移便可。具體可見代碼。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 300010; const ll inf = 1e16; ll f[N]; int n, m; struct tree { int l, r; ll mx, tag; } t[N << 2]; struct line { int l, r; ll v; } a[N]; #define lc (rt << 1) #define rc (rt << 1 | 1) void build(int l, int r, int rt) { t[rt].l = l; t[rt].r = r; if(l == r) return; int mid = (l + r) >> 1; build(l, mid, lc); build(mid + 1, r, rc); } void up(int rt) { t[rt].mx = max(t[lc].mx, t[rc].mx); } #define l (t[rt].l) #define r (t[rt].r) #define mid ((l + r) >> 1) void down(int rt) { if(t[rt].tag) { t[lc].mx += t[rt].tag; t[rc].mx += t[rt].tag; t[lc].tag += t[rt].tag; t[rc].tag += t[rt].tag; t[rt].tag = 0; } } void upd(int L, int R, ll c, int rt) { if(L <= l && r <= R) { t[rt].mx += c; t[rt].tag += c; return; } down(rt); if(L <= mid) upd(L, R, c, lc); if(R > mid) upd(L, R, c, rc); up(rt); } ll query(int L, int R, int rt) { if(L <= l && r <= R) return t[rt].mx; down(rt); ll ans = -inf; if(L <= mid) ans = max(ans, query(L, R, lc)); if(R > mid) ans = max(ans, query(L, R, rc)); return ans; } #undef l #undef r #undef mid #undef lc #undef rc bool operator < (line a, line b) { return a.r < b.r; } int main() { scanf("%d%d", &n, &m); build(1, n, 1); for(int i = 1; i <= m; ++i) { scanf("%d%d%lld", &a[i].l, &a[i].r, &a[i].v); } sort(a + 1, a + m + 1); int r = 1; for(int i = 1; i <= n; ++i) { upd(i, i, query(1, i, 1), 1); while(a[r].r == i) { upd(a[r].l, a[r].r, a[r].v, 1); ++r; } } printf("%lld\n", max(0LL, query(1, n, 1))); }
按\(w+s\)升序排序而後相似揹包轉移便可。
這個排序的貪心能夠用排序不等式證實。(也能夠感性理解)
#include <bits/stdc++.h> using namespace std; const int N = 1010; typedef long long ll; int n; ll f[N][N * 30]; struct Node { int w, s; ll v; } a[N]; /* 設f[i][j]表示前i個,而後總重量爲j的最大答案。 */ bool operator < (Node a, Node b) { if(a.s + a.w == b.s + b.w) return a.s < b.s; return a.s + a.w < b.s + b.w; } int main() { scanf("%d", &n); for(int i = 1; i <= n; ++i) { scanf("%d%d%lld", &a[i].w, &a[i].s, &a[i].v); } sort(a + 1, a + n + 1); for(int i = 1; i <= n; ++i) { for(int j = 0; j <= a[n].s + a[n].w; ++j) f[i][j] = max(f[i][j], f[i - 1][j]); for(int j = 0; j <= a[i].s; ++j) f[i][j + a[i].w] = max(f[i][j + a[i].w], f[i - 1][j] + a[i].v); } ll ans = 0; for(int i = 0; i <= a[n].s + a[n].w; ++i) ans = max(ans, f[n][i]); printf("%lld\n", ans); }
計數dp。首先一個沒有任何障礙的\(h\times w\)的網格圖從\((1,1)\)走到\((h,w)\)的方案數爲\(C^{h-1}_{h+w-2}\)(考慮一共要走\(h-1\)次向下,\(w-1\)次向右,一共\(h+w-2\)次操做)
設\(f[i]\)表示僅通過第\(i\)個黑點且目前位於第\(i\)個黑點的方案數。
將黑點按\(x\)爲第一關鍵字,\(y\)爲第二關鍵字升序排序。
有\(f[i]=C^{x_i-1}_{x_i+y_i-2}-\sum_{j<i,x_i\le x_j,y_i\le y_j}f[j]\times C^{x_i-x_j}_{x_i-x_j+y_i-y_j}\)
欽定\((h,w)\)爲第\(n+1\)個黑點,那麼答案就是\(f[n+1]\)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll mod = 1e9 + 7; const int N = 200010; ll fac[N], inv[N], f[N]; int h, w, n; struct Node { int x, y; } a[N]; ll power(ll a, ll b) { ll ans = 1; while(b) { if(b & 1) ans = ans * a % mod; a = a * a % mod; b >>= 1; } return ans; } bool operator < (Node a, Node b) { if(a.x == b.x) return a.y < b.y; return a.x < b.x; } ll C(int n, int m) { return fac[m] * inv[n] % mod * inv[m - n] % mod; } int main() { scanf("%d%d%d", &h, &w, &n); fac[0] = inv[0] = 1; for(int i = 1; i < N; ++i) { fac[i] = 1LL * fac[i - 1] * i % mod; inv[i] = power(fac[i], mod - 2); } for(int i = 1; i <= n; ++i) { scanf("%d%d", &a[i].x, &a[i].y); } sort(a + 1, a + n + 1); a[n + 1] = (Node) { h, w }; for(int i = 1; i <= n + 1; ++i) { f[i] = C(a[i].x - 1, a[i].x + a[i].y - 2); for(int j = 1; j < i; ++j) { if(a[j].x <= a[i].x && a[j].y <= a[i].y) { f[i] -= C(a[i].x - a[j].x, a[i].x - a[j].x + a[i].y - a[j].y) * f[j] % mod; (f[i] += mod) %= mod; } } } printf("%lld\n", f[n + 1]); }