(相信來看這篇博客的人都有題面)ios
T2覺得能夠線性遞推,而後花了兩個小時。而後想了兩個小時T1,會了一個能過的算法。可是沒時間寫,sad.....比賽快結束時,發現T2模數$10^9+7$,心裏mmp。算法
題目大意ide
給定一個$n\times m$的網格圖,每一個格子中有一個不是0就是1的數,要求對於任意$w\times h$的子矩陣的和都相等,問方案數。spa
(爲了簡潔,各式子的範圍請自行腦補)3d
瞎推推不難發現對於一個格子$(x, y)$,知足$a_{x, y} + a_{x + w, y + h} = a_{x + w, y} + a_{x, y + h}$。code
移項得:blog
$\begin{align}a_{x, y} - a_{x + w, y} = a_{x, y + h} - a_{x + w, y + h}\end{align}$get
用概括法可以得出對於每連續的$w + 1$行,對模$h$的剩餘相同的列,這$w + 1$行中的第一行和最後一行的上的數之差相等。博客
對於上圖來講就是$a - b = c - d = e - f$。it
考慮先肯定前$w$行,每次在後面添加一行。顯然這一行咱們只用考慮前$h$個數(剩下的用式$(1)$來肯定)。
考慮每一處$(i, j)$和它上面第$w$個格子上的數的差$d$
因此變化量能爲$1,-1$的只有當這一行上縱座標模$h$同餘於它的位置上的數等於它。
而後發現只要肯定左上角的$w\times h$的矩形的情況,剩下的就能用快速冪計算。
而後個人作法就比較沙雕了。
$f_{s}$表示剛好包含$s$中的位置做爲橫着相同的格子,且將前$w$行填滿的方案數。(不考慮這些格子上的數)
這個首先須要考慮這些格子上的數,進行容斥和子集和變換,再將這些格子上的數的貢獻減去。
而後單獨考慮這個矩陣中每一行的貢獻,枚舉$0$和$1$的數量,就能夠計算貢獻了。
1 #include <iostream> 2 #include <cassert> 3 #include <cstdlib> 4 #include <cstdio> 5 using namespace std; 6 typedef bool boolean; 7 8 const int Mod = 1e9 + 7; 9 10 int add(int a, int b) { 11 return ((a += b) >= Mod) ? (a - Mod) : (a); 12 } 13 14 int sub(int a, int b) { 15 return ((a -= b) < 0) ? (a + Mod) : (a); 16 } 17 18 int mul(int a, int b) { 19 return a * 1ll * b % Mod; 20 } 21 22 int qpow(int a, int p) { 23 int rt = 1, pa = a; 24 for ( ; p; p >>= 1, pa = mul(pa, pa)) 25 if (p & 1) 26 rt = mul(rt, pa); 27 return rt; 28 } 29 30 const int S = 1 << 16; 31 32 int n, m, w, h; 33 int f[S], bit[S]; 34 int c[5][5], sc[5][5]; 35 int d[5][5], sd[5][5]; 36 int comb[5][5] = {{1, 0, 0, 0, 0}, {1, 1, 0, 0, 0}, {1, 2, 1, 0, 0}, {1, 3, 3, 1, 0}, {1, 4, 6, 4, 1}}; 37 int invs[17]; 38 39 inline void init() { 40 scanf("%d%d%d%d", &n, &m, &w, &h); 41 } 42 43 int getline(int s, int l) { 44 int rt = (s >> l) & 1; 45 for (int i = 1; i < w; i++) 46 rt = (((s >> (h * i + l)) & 1) << i) | rt; 47 return rt; 48 } 49 50 int getrow(int s, int row) { 51 int msk = (1 << h) - 1; 52 return s >> (h * row) & msk; 53 } 54 55 inline void solve() { 56 d[0][0] = 1; 57 for (int i = 0; i < 4; i++) 58 for (int j = 0; j <= i; j++) { 59 d[i + 1][j] = add(d[i + 1][j], d[i][j]); 60 d[i + 1][j + 1] = add(d[i + 1][j + 1], d[i][j]); 61 } 62 63 int sl_repeat = m / h; 64 for (int i = 0; i <= 4; i++) 65 for (int j = 0; j <= i; j++) 66 sd[i][j] = qpow(d[i][j], sl_repeat); 67 68 bit[0] = 0; 69 for (int i = 0; i < S; i++) 70 bit[i] = bit[i >> 1] + (i & 1); 71 72 int all = 1 << (w * h); 73 for (int s = 0; s < all; s++) { 74 f[s] = (1 << bit[s]); 75 for (int l = 0; l < h; l++) { 76 int sl = getline(s, l); 77 int blank = w - bit[sl]; 78 int cmp = 0; 79 boolean aflag = ((m % h) > l); 80 for (int i = 0; i <= blank; i++) 81 cmp = add(mul(sd[blank][i], (aflag) ? (d[blank][i]) : (1)), cmp); 82 f[s] = mul(f[s], cmp); 83 } 84 } 85 86 int size = w * h; 87 for (int i = 0; i < size; i++) 88 for (int j = 0; j < all; j++) 89 if (!((j >> i) & 1)) 90 f[j] = sub(f[j], f[j ^ (1 << i)]); 91 92 for (int k = 0; k <= 4; k++) 93 for (int c0 = 0; c0 <= k; c0++) { 94 int c1 = k - c0, ava = min(c0, c1); 95 int& res = c[c0][c1]; 96 for (int use = 0; use <= ava; use++) { 97 // res = add(res, mul(comb[c0][use], mul(comb[c1][use], fac[use]))); 98 res = add(res, mul(comb[c0][use], comb[c1][use])); 99 } 100 sc[c0][c1] = qpow(res, n / w - 1); 101 } 102 103 invs[0] = 1; 104 for (int i = 1; i <= 16; i++) 105 invs[i] = qpow(1 << i, Mod - 2); 106 for (int i = 0; i < all; i++) 107 f[i] = mul(f[i], invs[bit[i]]); 108 109 int res = 0; 110 for (int s = 0; s < all; s++) { 111 if (!f[s]) 112 continue; 113 int tmp = 1; 114 for (int r = 0; r < w; r++) { 115 int sr = getrow(s, r); 116 int spe = bit[sr], cmp = 0; 117 boolean aflag = ((n % w) > r); 118 for (int c0 = 0; c0 <= spe; c0++) { 119 cmp = add(cmp, mul(mul(::sc[c0][spe - c0], ((aflag) ? (c[c0][spe - c0]) : (1))), comb[spe][c0])); 120 } 121 tmp = mul(tmp, cmp); 122 // cerr << sr << " " << s << " " << r << " " << cmp << '\n'; 123 } 124 // if (tmp && f[s]) 125 // cerr << s << " " << mul(f[s], tmp) << '\n'; 126 res = add(res, mul(f[s], tmp)); 127 } 128 printf("%d\n", res); 129 } 130 131 int main() { 132 init(); 133 solve(); 134 return 0; 135 }
題目大意
有$n$種物品,每種物品無限個,第$i$種物品的體積爲$a_i$。設$f(n)$表示剛好填滿容量爲$n$的揹包的方案數,求$\sum_{i = L}^{R}f(i)$。
(這道題的名稱告訴了咱們不想掉rating的正確作法)
(咱們能夠線性遞推 + 三模數NTT)
考慮每加入一個物品至關於對模$a_i$同餘於$j\ (j = 0, 1, \dots, a_i - 1)$的地方分別作一次前綴和。
因此對於模$[a_{1}, a_{2}, \cdots, a_{n}]$餘$r$的地方能夠用一個$n - 1$次多項式表示。
因爲要求前綴和,就再加入體積爲$1$的物品。
對於$L-1,R$處的取值直接用$Lagrange$插值。
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstdio> 4 using namespace std; 5 typedef bool boolean; 6 7 #define ll long long 8 9 const int N = 11, S = 100001 * N; 10 const int Mod = 1e9 + 7; 11 12 int add(int a, int b) { 13 return ((a += b) >= Mod) ? (a - Mod) : (a); 14 } 15 16 int sub(int a, int b) { 17 return ((a -= b) < 0) ? (a + Mod) : (a); 18 } 19 20 int mul(int a, int b) { 21 return a * 1ll * b % Mod;; 22 } 23 24 void exgcd(int a, int b, int& x, int& y) { 25 if (!b) 26 x = 1, y = 0; 27 else { 28 exgcd(b, a % b, y, x); 29 y -= (a / b) * x; 30 } 31 } 32 33 int inv(int a, int n) { 34 int x, y; 35 exgcd(a, n, x, y); 36 return (x < 0) ? (x + n) : (x); 37 } 38 39 int n, prod = 1, m; 40 int a[N], f[S]; 41 42 inline void init() { 43 scanf("%d", &n); 44 for (int i = 0; i < n; i++) 45 scanf("%d", a + i), prod *= a[i]; 46 m = prod * (n + 1); 47 f[0] = 1; 48 for (int j = 0; j < n; j++) 49 for (int i = 1; i <= m; i++) 50 if (a[j] <= i) 51 f[i] = add(f[i - a[j]], f[i]); 52 for (int i = 1; i <= m; i++) 53 f[i] = add(f[i], f[i - 1]); 54 } 55 56 int Lagrange(ll _x) { 57 if (_x <= m) 58 return f[_x]; 59 int k = _x % prod; 60 int x = (_x / prod) % Mod; 61 int rt = 0; 62 for (int i = 0; i <= n; i++) { 63 int tmp = f[i * prod + k]; 64 for (int j = 0; j <= n; j++) 65 if (i ^ j) 66 tmp = mul(tmp, mul(sub(x, j), inv(sub(i, j), Mod))); 67 rt = add(rt, tmp); 68 } 69 return rt; 70 } 71 72 ll l, r; 73 inline void solve() { 74 scanf("%lld%lld", &l, &r); 75 printf("%d\n", sub(Lagrange(r), Lagrange(l - 1))); 76 } 77 78 int main() { 79 init(); 80 solve(); 81 return 0; 82 }
題目大意
要求構造一個點數不超過$50$,邊數不超過$100$的有向圖,使得它的本質不一樣的拓撲序的個數爲$x$。
當$x = 1,2$的時候特判。
當$x$不大的時候,構造一條鏈和一個點就好了。
當$x$比較大的時候,考慮這樣一個東西:
考慮在加入紫色節點前,以紅色節點結尾的拓撲序的個數爲$x$,以綠色節點結尾的拓撲序的個數爲$y$,如圖所示加入紫色節點,那麼不可貴到以紫色節點爲結尾的拓撲序有$x + y$個。
那麼就能夠直接爆搜了。
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstdio> 4 #include <vector> 5 using namespace std; 6 typedef bool boolean; 7 8 #define pii pair<int, int> 9 10 const int Lim = 50; 11 12 int X; 13 14 inline void init() { 15 scanf("%d", &X); 16 } 17 18 int type[Lim + 5]; 19 vector<pii> E; 20 void dfs(int x, int y, int d, int lim) { 21 if (d == lim || d == Lim || x + y > X) 22 return; 23 if (x + y == X) { 24 vector<int> L(2), R(2); 25 L[0] = 0, L[1] = 1, R[0] = 2, R[1] = 3; 26 E.push_back(pii(0, 1)); 27 E.push_back(pii(2, 3)); 28 E.push_back(pii(0, 3)); 29 for (int i = 4; i < d; i++) 30 if (!type[i]) { 31 E.push_back(pii(L.back(), i)); 32 E.push_back(pii(R[R.size() - 2], i)); 33 L.push_back(i); 34 } else { 35 E.push_back(pii(R.back(), i)); 36 E.push_back(pii(L[L.size() - 2], i)); 37 R.push_back(i); 38 } 39 40 printf("%d %d\n", d, (signed) E.size()); 41 for (int i = 0; i < (signed) E.size(); i++) 42 printf("%d %d\n", E[i].first, E[i].second); 43 exit(0); 44 } 45 46 type[d] = 0; 47 dfs(x + y, y, d + 1, lim); 48 type[d] = 1; 49 dfs(x, x + y, d + 1, lim); 50 } 51 52 inline void solve() { 53 if (X == 1) { 54 printf("2 1\n0 1\n"); 55 } else if (X == 2) { 56 printf("3 2\n0 1\n2 1\n"); 57 }else if (X <= 40) { 58 printf("%d %d\n", X, X - 2); 59 for (int i = 0; i < X - 2; i++) 60 printf("%d %d\n", i, i + 1); 61 } else { 62 for (int lim = 7; lim <= 50; lim++) 63 dfs(2, 3, 4, lim); 64 puts("Failed"); 65 } 66 } 67 68 int main() { 69 init(); 70 solve(); 71 return 0; 72 }