「考前日志」11.17
总结
最近真的很不对劲
打不起精神头来
而且电脑还巨卡
一天做不了几道题
挺压抑的
上一次这种情况是在三月份了……
我该怎么办啊
⑨⑨⑤⑧
今日已完成
-
AcWing291 蒙德里安的梦想
状压DP。
以行数以及此行的形态为状态。
设 \(f_{i,j}\) 表示前 \(i\) 行,第 \(i\) 行形态为 \(j\) 时的方案总数。
此处 \(j\) 是一个用十进制整数记录的 \(m\) 位二进制数。
如果 \(j\) 二进制下当前位置为 \(1\),说明该位置为某个小长方形的上半部分,下一行的当前位置一定要放下半部分(即为 \(0\))。
如果为 \(0\) 表示其他情况,对下一行的形态无影响,但要保证连续的 \(0\) 的个数为偶数个。
对于当前行 \(i\) 的形态 \(j\),可以由上一行 \(i- 1\) 的形态 \(k\) 转移过来当且仅当:- 当前行的形态 \(j\) 与上一行的形态 \(k\) 的与运算结果为 \(0\)。
这样保证了上一行形态中为 \(1\) 的位对应的当前位一定为 \(0\),满足上述条件。 - \(j\) 和 \(k\) 的按位或运算的二进制表示中连续 \(0\) 的个数为偶数个。
这样也就说明\(j\) 和 \(k\) 的二进制表示中连续 \(0\) 的个数为偶数个。
预处理合法(即连续 \(0\) 为偶数)的状态,然后 dp 即可。
\[f_{i,j}=\sum\limits_{j\&k=0且j|k合法}f_{i-1,k} \]#include <map> #include <cmath> #include <queue> #include <cstdio> #include <vector> #include <cstring> #include <iostream> #include <algorithm> #define ll long long using namespace std; const int A = 1e5 + 11; const int B = 1e6 + 11; const int mod = 1e9 + 7; const int inf = 0x3f3f3f3f; inline int read() { char c = getchar(); int x = 0, f = 1; for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1; for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48); return x * f; } int n, m; bool ok[A]; ll f[12][1 << 11]; int main() { while (cin >> n >> m) { if (n == 0 && m == 0) return 0; memset(f, 0, sizeof(f)); for (int i = 0; i < (1 << m); i++) { bool cnt = 0, has_odd = 0; for (int j = 0; j < m; j++) if (i >> j & 1) has_odd |= cnt, cnt = 0; else cnt ^= 1; ok[i] = !(has_odd | cnt); } f[0][0] = 1; for (int i = 1; i <= n; i++) { for (int j = 0; j < (1 << m); j++) { for (int k = 0; k < (1 << m); k++) if ((j & k) == 0 && ok[j | k]) f[i][j] += f[i - 1][k]; } } cout << f[n][0] << '\n'; } }
- 当前行的形态 \(j\) 与上一行的形态 \(k\) 的与运算结果为 \(0\)。
-
AcWing289 环路运输
环形DP
还是断环成链的操作
把环拆开,复制一倍,形成一个长度为 \(2n\) 的链
那么就是要求最大的 \(1\le{i,j}\le{2n}\) 且 \({i-j}\le{\dfrac{n}{2}}\) 的 \(i,j\),\(a_i+a_j+i-j\) 的最大值
可以用单调队列优化,做到 \(O(n)\) 的复杂度#include <map> #include <cmath> #include <queue> #include <cstdio> #include <vector> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int A = 2e6 + 11; const int B = 1e6 + 11; const int mod = 1e9 + 7; const int inf = 0x3f3f3f3f; inline int read() { char c = getchar(); int x = 0, f = 1; for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1; for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48); return x * f; } int n, head, tail, len, a[A], q[A], ans; int main() { n = read(); for (int i = 1; i <= n; i++) a[i] = read(), a[i + n] = a[i]; len = n / 2, head = 1, tail = 0; q[++tail] = a[1]; for (int i = 2; i <= n * 2; i++) { while (head <= tail && q[head] < i - len) head++; ans = max(ans, i + a[i] + a[q[head]] - q[head]); while (head <= tail && a[q[tail]] - q[tail] < a[i] - i) tail--; q[++tail] = i; } cout << ans << '\n'; return 0; }
-
牛客编程巅峰赛S2第2场 - 钻石&王者T1
函数式编程难受,机房电脑性能辣鸡class Solution { public: /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * @param n long长整型 表示标准完全二叉树的结点个数 * @return long长整型 */ long long tree4(long long n) { // write code here long long mod = 998244353; long long base = 0, x = 0, ans = 0; while (x + (1 << base) <= n) { long long now = x + (1 << base); ans = ans + (1ll * now * (now + 1) / 2 % mod - 1ll * x * (x + 1) / 2 % mod + mod) % mod * (base + 1); ans = ans % mod; x = now, base++; } if (x == n) return ans; ans += (1ll * n * (n + 1) / 2 % mod - x * (x + 1) / 2 % mod + mod) % mod * (base + 1) % mod; ans %= mod; return ans; } };
-
牛客编程巅峰赛S2第2场 - 钻石&王者T2
函数式编程难受,机房电脑性能辣鸡,不会写高精/kk
class Solution { public: /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * 返回最大和的字符串 * @param x string字符串 即题目描述中所给字符串 * @param k int整型 即题目描述中所给的k * @return string字符串 */ string Maxsumforknumers(string x, int k) { int vis[100] = { 0 }; int n = x.size(), ans[100001] = { 0 }, len = 0; string s; for (int i = 0; i < n; i++) vis[x[i] - '0']++; for (int i = 1; i <= n - k + 1; i++) { int we = 0; for (int j = 9; j >= 0; j--) if (vis[j]) { we = j, vis[we]--; break; } ans[i] = we; } len = n - k + 1; reverse(ans + 1, ans + len + 1); int we = 0; for (int j = 0; j <= 9; j++) while (vis[j]) { vis[j]--, we = j; int po = 1; ans[po] += j; while (ans[po] >= 10) ans[po] -= 10, po++, ans[po]++, len = max(len, po); } reverse(ans + 1, ans + len + 1); for (int i = 1; i <= len; i++) s += ans[i] + '0'; return s; } };
-
AcWing292 炮兵阵地
用 \(f_{i,j,k}\) 表示已经摆完前 \(i\) 行,且所有摆放的炮兵之间不能相互攻击到,每个炮兵都不在山地上,第 \(i\) 行的状态为 \(j\),第 \(i-1\) 行的状态为 \(k\) 的方案数。
因为第 \(i\) 行和第 \(i-1\) 行的状态已经确定了,但是当前阶段还和第 \(i-2\) 行的状态有关,所以要枚举第 \(i-2\) 行的状态,记为 \(u\)。
什么时候状态是合法的呢?
- \(j,k,u\) 三者表示的状态无交集。
- 第 \(i\) 行的炮兵没有摆放到山地上。
- 第 \(i\) 行的炮兵两两之间的距离\(\ge2\)。
显然满足上述条件的状态就是合法的。
转移: \(f_{i,j,k}=f_{i-1,j,u}+sum_i\)
其中 \(sum_i\) 表示第 \(i\) 行可以摆放的炮兵的个数。
#include <cmath> #include <queue> #include <cstdio> #include <vector> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int A = 111; const int B = 11; const int S = 1 << 11; const int mod = 1e9 + 7; const int inf = 0x3f3f3f3f; inline int read() { char c = getchar(); int x = 0, f = 1; for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1; for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48); return x * f; } vector <int> sta; int n, m, g[A], sum[S], f[2][S][S]; bool check(int s) { for (int i = 0; i < m; i++) if (((s >> i) & 1) && (((s >> (i + 1)) & 1) || (s >> (i + 2) & 1))) return false; return true; } int count(int x) { int cnt = 0; while (x) { cnt += x & 1; x >>= 1; } return cnt; } int main() { n = read(), m = read(); for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { char c; cin >> c; if (c == 'H') g[i] += (1 << j); } } for (int i = 0; i < 1 << m; i++) { if (check(i)) { sta.push_back(i); sum[i] = count(i); } } // cout << sta.size() << '\n'; for (int i = 0; i < n + 2; i++) { for (int k = 0; k < sta.size(); k++) for (int j = 0; j < sta.size(); j++) for (int u = 0; u < sta.size(); u++) { int a = sta[u], b = sta[j], c = sta[k]; if ((a & b) || (a & c) || (b & c)) continue; if (g[i] & c) continue; f[i & 1][k][j] = max(f[i & 1][k][j], f[i - 1 & 1][j][u] + sum[c]); } } cout << f[n + 1 & 1][0][0] << '\n'; }