NOIP模拟测试20
Liu_runda聚聚的馈赠(
Problem A: 周
防自闭题?
这道题让我整个考试都很愉悦(
搜就完事了
1 #include <bits/stdc++.h> 2 3 int n; 4 int a[20], b[20], c[20], d[20]; 5 long long ans; 6 7 void dfs(int day, long long oi, long long whk) { 8 if (day == n + 1) { 9 ans = std::max(ans, oi * whk); 10 return; 11 } 12 long long x, y; 13 x = oi - b[day] < 0 ? 0 : oi - b[day], y = whk + a[day]; 14 dfs(day + 1, x, y); 15 x = oi + c[day], y = whk - d[day] < 0 ? 0 : whk - d[day]; 16 dfs(day + 1, x, y); 17 } 18 19 signed main() { 20 scanf("%d", &n); 21 for (int i = 1; i <= n; i++) 22 scanf("%d%d%d%d", &a[i], &b[i], &c[i], &d[i]); 23 dfs(1, 0, 0); 24 printf("%lld\n", ans); 25 return 0; 26 }
Problem B: 任
题目保证黑点之间只有一条简单路径,显然相邻黑点连边就是一颗树。
求森林有几棵树:总点数-边数。
二维前缀和ning干:记点的前缀和、行上边的前缀和、列上边的前缀和、边的前缀和。
加加减减乱搞一下就有了。
1 #include <bits/stdc++.h> 2 3 int n, m, q; 4 int color[2005][2005], sum[2005][2005], edge[2005][2005]; 5 int row[2005][2005], col[2005][2005]; 6 7 signed main() { 8 scanf("%d%d%d", &n, &m, &q); 9 for (int i = 1; i <= n; i++) { 10 for (int j = 1; j <= m; j++) { 11 scanf("%1d", &color[i][j]); 12 row[i][j] = (color[i][j-1] + color[i][j]) == 2; 13 col[i][j] = (color[i-1][j] + color[i][j]) == 2; 14 sum[i][j] = sum[i-1][j] + sum[i][j-1] - sum[i-1][j-1]; 15 edge[i][j] = edge[i-1][j] + edge[i][j-1] - edge[i-1][j-1]; 16 sum[i][j] += color[i][j]; 17 edge[i][j] += row[i][j] + col[i][j]; 18 } 19 } 20 for (int i = 1; i <= n; i++) 21 for (int j = 1; j <= m; j++) 22 row[i][j] += row[i][j-1]; 23 for (int j = 1; j <= m; j++) 24 for (int i = 1; i <= n; i++) 25 col[i][j] += col[i-1][j]; 26 for (int i = 1; i <= q; i++) { 27 int x_1, x_2, y_1, y_2; 28 scanf("%d%d%d%d", &x_1, &y_1, &x_2, &y_2); 29 int point = sum[x_2][y_2] - sum[x_1-1][y_2] - 30 sum[x_2][y_1-1] + sum[x_1-1][y_1-1]; 31 int edg = edge[x_2][y_2] - edge[x_1][y_2] - edge[x_2][y_1] + edge[x_1][y_1]; 32 int waste = row[x_1][y_2] + col[x_2][y_1] - row[x_1][y_1] - col[x_1][y_1]; 33 printf("%d\n", point - edg - waste); 34 } 35 return 0; 36 }
Problem C: 飞
2h做一道题((
首先能看出来是求逆序对数。
恶意的出题人把size卡到32M Orz
本题专属奇妙性质:x在每一段是一个等差数列,而且题目保证x互不相同。
首先他有一个固定的形式:$x_{i+1} = x_i + a$。
我们就钦定它小于P,大于再说(
我们再钦定和x_i组成的逆序对数为m,之前已经组成了k个等差数列。
由于$x_{i+1}$小于P,之前的每个等差数列都必定存在一个数大于$x_i$小于$x_{i+1}$,因此每个等差数列会少一对逆序对,与$x_{i+1}$组成的逆序对数为m-k。
递推总要有个起点8?根据等差数列的首项确定等差数列。因此树状数组还是要开的,不过在a上开就行,这样空间就够用了。
对拍的时候发现一个坑:可能第一个等差数列起点就比$x_i$大了,特判掉就吼了(
1 #include <bits/stdc++.h> 2 3 const int N = 1e5 + 3; 4 int n, x_1, a, mod, now, chaos, sairai; 5 long long ans = 0; 6 7 struct BIT { 8 int tr[N]; 9 void add(int x) { 10 ++x; 11 for (; x < N; x += x & -x) ++tr[x]; 12 } 13 int ask(int x) { 14 int ret = 0; 15 ++x; 16 for (; x; x -= x & -x) ret += tr[x]; 17 return ret; 18 } 19 } bit; 20 21 signed main() { 22 scanf("%d%d%d%d", &n, &x_1, &a, &mod); 23 now = x_1; 24 for (int i = 1; i <= n; ++i) { 25 if (now < a) { 26 chaos = i - bit.ask(now) - 1; 27 ans += chaos; 28 bit.add(now); 29 } else { 30 chaos -= sairai; 31 if (x_1 > now) ++chaos; 32 ans += chaos; 33 } 34 now += a; 35 if (now >= mod) { 36 now -= mod; 37 ++sairai; 38 } 39 } 40 printf("%lld\n", ans); 41 return 0; 42 }
$ \Theta \omega \Theta $