2019-08-17 纪中NOIP模拟B组
T1 [JZOJ3503] 粉刷
题目描述
鸡腿想到了一个很高(sha)明(bi)的问题,墙可以看作一个 $N \times M$ 的矩阵,有一些格子是有污点的。现在鸡腿可以竖着刷一次,覆盖连续的最多 $C$ 列,或者横着刷一次,覆盖连续的最多R行。现在鸡腿把墙上的情况告诉你,请你告诉鸡腿最少要刷多少次才能刷干净!
数据范围
对于 $50\%$ 的数据,$1 \leq N,M \leq 5$
对于 $100\%$ 的数据,$1 \leq N,M,R,C \leq 15$
分析
临时换题什么鬼啊 还有本题正解暴力??
枚举横着刷的所有情况,然后对于每种情况竖着刷完剩下的点
//结束前十分钟开始打 写得不是很好看 #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <queue> using namespace std; #define ll long long #define inf 0x3f3f3f3f #define N 16 int n, m, r, c, ans = inf; int sum[N], line[N]; char g[N][N]; void dfs(int x, int t, int last) { if (last) { int now = t; for (int i = 1; i <= m; i++) if (sum[i]) i += c - 1, now++; ans = min(ans, now); } if (x > n) return; dfs(x + 1, t, 0); for (int i = x; i <= x + r - 1 && i <= n; i++) for (int j = 1; j <= m; j++) if (g[i][j] == 'X') sum[j]--; dfs(x + c, t + 1, 1); for (int i = x; i <= x + r - 1 && i <= n; i++) for (int j = 1; j <= m; j++) if (g[i][j] == 'X') sum[j]++; } int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { scanf(" %c", &g[i][j]); if (g[i][j] == 'X') sum[j]++, line[i]++; } scanf("%d%d", &r, &c); dfs(1, 0, 1); printf("%d\n", ans); return 0; }
T2 [JZOJ3504] 运算符
题目描述
鸡腿想到了一个很高(sha)明(bi)的运算符,那就是 '$!$',没错就是感叹号。他给了如下的定义:
1. $n!k = n!(k-1) \times (n-1)!k \;\; (n > 0 \; and \; k > 0)$
2. $n!k = 1 \;\; (n = 0)$
3. $n!k = n \;\; (k = 0)$
现在鸡腿告诉你n和k你能告诉他n!k的不同约数个数有多少个吗?只要对1,000,000,009取模就可以了哦!
数据范围
对于 $30\%$ 的数据,$0 \leq N \leq 10$,$0 \leq K \leq 10$
对于 $100\%$ 的数据,$0 \leq N \leq 1000$,$0 \leq K \leq 100$
分析
首先很容易发现对答案有贡献的只有 $(i,0) \; (1 \leq i \leq n)$
有一种思路就是算出所有 $(i,0)$ 会出现的次数,然后就可以算出 $n!k$ 中所有质因子的次数
由于 $(i,0)$ 只可能从 $(i,1)$ 转移过来,所以 $(i,0)$ 出现的次数就等于从 $(n,k)$ 转移到 $(i,0)$ 的不同操作顺序的种类
每次操作可以让前面的数减一或者是后面的数减一,这就很像从矩形左上角走到右下角只能向右或向下走的不同路径总数
前面的数一共要减 $n-i$ 次,后面的数一共要减 $k-1$ 次,所以总数为 $\binom{n-i+k-1}{k-1}$
然后就是先埃氏筛出 $n$ 以内的质数,再把 $1 \sim n$ 的所有数质因数分解,将每项质因子次数乘上 $(i,0)$ 出现的次数计入质因子的总次数
最后就是这个求因子个数的公式了 $f(n)= \prod_{i=1}^k (a_i+1)$
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <queue> using namespace std; #define ll long long #define inf 0x3f3f3f3f #define N 1005 int n, k, tot, p = 1e9 + 9; int prm[N], book[N]; ll c[N], inv[N], t[N], ans = 1; void pre_Prm() { for (int i = 2; i <= n; i++) if (!book[i]) { prm[++tot] = i; for (int j = 2; i * j <= n; j++) book[i * j] = 1; } } void pre_C() { inv[1] = 1; c[n] = 1; for (int i = 2; i <= n; i++) inv[i] = (p - p / i) * inv[p % i] % p; for (int i = n - 1; i; i--) c[i] = (c[i + 1] * (n - i + k - 1) % p) * inv[n - i] % p; } int main() { scanf("%d%d", &n, &k); pre_Prm(); pre_C(); for (int i = 1; i <= n; i++) for (int j = 1, k = i; j <= tot && k; j++) { int sum = 0; while (!(k % prm[j])) k /= prm[j], sum++; t[j] = (t[j] + sum * c[i] % p) % p; } for (int i = 1; i <= tot; i++) ans = ans * (t[i] + 1) % p; printf("%lld\n", ans); return 0; }
T3 [JZOJ6290] 倾斜的线
题目描述
数据范围
分析
$O(n^2)$ 做法很简单,就是枚举所有斜率
但实际上很多枚举的斜率对答案是不可能产生贡献的
题目是要求出 $\vert \frac{y_2-y_1}{x_2-x_1}-\frac{P}{Q} \vert$ 的最小值
将式子通分后得到 $\vert \frac{(Qy_2-Px_2)-(Qy_1-Px_1)}{Qx_2-Qx_1} \vert$
我们会发现这很像一个斜率的表达式,此时的点坐标为 $(Qx,Qy-Px)$
所以这道题就转化为了求这些点之间的斜率绝对值的最小值
对于任意两个点,如果存在另一点在两者纵坐标之间,那么这两点之间的边一定不是绝对值最小的斜率
这说明最优解一定是在所有纵坐标相邻点之间的斜率中
这样就可以先按纵坐标排序,然后算出所有相邻点的斜率来更新答案
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <queue> using namespace std; #define ll long long #define inf 0x3f3f3f3f #define N 200005 int n; ll P, Q, p, q; double k = 1000000000, lastk; struct Point {ll x, y; int num;} s[N], t[N]; ll gcd(ll a, ll b) {return !b ? a : gcd(b, a % b);} bool cmp(Point a, Point b) {return a.y < b.y;} double Abs(double x) {return x < 0 ? -x : x;} double getk(Point a, Point b) { return (double)(b.y - a.y) / (double)(b.x - a.x); } int main() { freopen("slope.in", "r", stdin); freopen("slope.out", "w", stdout); scanf("%d%d%d", &n, &P, &Q); for (int i = 1; i <= n; i++) { scanf("%lld%lld", &s[i].x, &s[i].y); t[i].x = Q * s[i].x; t[i].y = Q * s[i].y - P * s[i].x; t[i].num = i; } sort(t + 1, t + n + 1, cmp); for (int i = 1; i < n; i++) { double nowk = Abs(getk(t[i], t[i + 1])); if (nowk < k) { k = nowk; p = s[t[i + 1].num].y - s[t[i].num].y; q = s[t[i + 1].num].x - s[t[i].num].x; lastk = getk(s[t[i].num], s[t[i + 1].num]); } else if (nowk == k) { nowk = getk(s[t[i].num], s[t[i + 1].num]); if (nowk < lastk) { lastk = nowk; p = s[t[i + 1].num].y - s[t[i].num].y; q = s[t[i + 1].num].x - s[t[i].num].x; } } } p = Abs(p); q = Abs(q); ll g = gcd(p, q); printf("%lld/%lld\n", p / g, q / g); return 0; }