总结
最近真的很不对劲
打不起精神头来
而且电脑还巨卡
一天做不了几道题
挺压抑的
上一次这种情况是在三月份了……
我该怎么办啊
⑨⑨⑤⑧
今日已完成
-
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}=sumlimits_{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] << ' '; } }
- 当前行的形态 (j) 与上一行的形态 (k) 的与运算结果为 (0)。
-
AcWing289 环路运输
环形DP
还是断环成链的操作
把环拆开,复制一倍,形成一个长度为 (2n) 的链
那么就是要求最大的 (1le{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 << ' '; 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() << ' '; 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] << ' '; }