2024.2.26
前言
还有
博弈
以为是个吊题,结果真是签到题啊QAQ。
首先我们要读明白题,我们一个点可以放多个棋子,所以可以得出一个结论:每个点是互不影响的。
所以我们可以每个点分开来算。
正如题解所说:“因为在自己所属点上的棋子是完全由自己操控的,不会莫名其妙地被对方抢走”,所以最优策略就是让自己可以走的步数减去对面可以走的步数。
然后我们就可以很轻易的得到一个
注意要把每个
最后的统计答案的时候背包,找大于
#include <bits/stdc++.h> const int SIZE = 310; const int mod = 998244353; int N, M; int cnt = 1, head[SIZE], to[SIZE * SIZE], next[SIZE * SIZE]; int degree[SIZE], dp[SIZE], f[2 * SIZE * SIZE]; char str[SIZE]; bool color[SIZE]; void AddEdge(int u, int v) { ++ cnt; next[cnt] = head[u]; head[u] = cnt; to[cnt] = v; } int main() { std::ios::sync_with_stdio(false); std::cin.tie(0); std::cout.tie(0); freopen("game.in", "r", stdin); freopen("game.out", "w", stdout); std::cin >> N >> M; std::cin >> (str + 1); for (int i = 1; i <= N; ++ i) color[i] = (str[i] == 'W' ? 0 : 1); for (int i = 1, x, y; i <= M; ++ i) { std::cin >> x >> y; if (x < y) std::swap(x, y); degree[y] ++; AddEdge(x, y); } for (int now = N; now >= 1; -- now) { for (int i = head[now]; i; i = next[i]) { if (color[to[i]] == 0) dp[to[i]] = std::max(dp[to[i]], dp[now] + 1); if (color[to[i]] == 1) dp[to[i]] = std::min(dp[to[i]], dp[now] - 1); } } int begin = 301 * 301; f[begin] = 1; for (int i = 1; i <= N; ++ i) { if (dp[i] < 0) { for (int j = begin - 300 * 300; j <= begin + 300 * 300; ++ j) { if (f[j] == 0) continue; f[j + dp[i]] = (f[j + dp[i]] + f[j]) % mod; } } else { for (int j = begin + 300 * 300; j >= begin - 300 * 300; -- j) { if (f[j] == 0) continue; f[j + dp[i]] = (f[j + dp[i]] + f[j]) % mod; } } } int answer = 0; for (int i = begin + 1; i <= begin + 300 * 300; ++ i) answer = (answer + f[i]) % mod; std::cout << answer << '\n'; return 0; }
排列
呃呃呃,这道题属实有些逆天了。
依靠人类智慧,我们发现对于题目的限制,我们可以将点对
然后我们称一个区间是好(或区间是有传递性的)的,当且仅当在一个区间内,若
这样我们对于一个题目要求的合法排列一定存在:排列任意的前缀和任意的后缀都是好的。
然鹅上面和正解关系不大
根据爆搜,我们可以知道对于一个
那我们怎么维护呐?
继续人类智慧QAQ:
我们先拿出来的个
对于题目里要求的情况,我们每次交换
洛谷
// ubsan: undefined // accoders #include <bits/stdc++.h> typedef long long ll; const int mod = 998244353; int N, M; std::vector<std::pair<int, int>> limit[11][11]; std::vector<int> edge[4000000]; int array[11], pos[11], small[11]; ll fac[11], dp[4000000]; int main() { std::ios::sync_with_stdio(false); std::cin.tie(0); std::cout.tie(0); freopen("perm.in", "r", stdin); freopen("perm.out", "w", stdout); std::cin >> N >> M; for (int i = 1, a, b, c, d; i <= M; ++i) { std::cin >> a >> b >> c >> d; limit[c][d].emplace_back(a, b); } fac[0] = 1; for (int i = 1; i <= N; ++i) { fac[i] = fac[i - 1] * i; array[i] = i; } int id = 0; do { ++id; for (int i = 1; i <= N; ++i) { pos[array[i]] = i; small[i] = 0; for (int j = i + 1; j <= N; ++j) if (array[i] > array[j]) ++small[i]; } for (int i = 2; i <= N; ++i) { if (array[i - 1] > array[i]) continue; int c = array[i - 1]; int d = array[i]; bool flag = true; for (const std::pair<int, int> &iter : limit[c][d]) { if (pos[iter.first] < pos[iter.second]) { flag = false; break; } } if (!flag) continue; int to = id; to -= fac[N - i] * small[i]; to -= fac[N - i + 1] * small[i - 1]; to += fac[N - i] * small[i - 1]; to += fac[N - i + 1] * (small[i] + 1); // std::cout << id << ' ' << to << ' ' << i - 1 << ' ' << i << '\n'; // for (int j = 1; j <= N; ++ j) // std::cout << small[j] << ' '; // std::cout << '\n'; edge[id].emplace_back(to); } } while (std::next_permutation(array + 1, array + 1 + N)); dp[1] = 1; for (int i = 1; i <= id; ++i) for (const int &to : edge[i]) dp[to] = (dp[to] + dp[i]) % mod; std::cout << dp[id] << '\n'; return 0; }
子段和
先想一想暴力。
我们发现:若我们现在求删完了
然后我就不会了。
但是题解告诉我:我们二分答案最后的最小值就行了。
我的内心:哈哈_,你真幽默。
具体说说做法。
暴力写法1
我们每次二分答案可以到达的最小值。
在二分的
注意我们对于每个点减去的值,在这个点之后的前缀和里面都要减去。
最后判断减去的值打不大于我们的
但是我的写法好像没办法优化。
csdn
#include <bits/stdc++.h> typedef long long ll; const int SIZE = 1100; const int mod = 998244353; int N, K; ll a[SIZE], pre[SIZE], answer; bool check(int max, int rest) { max -= 2e7; ll min = 0, differ = 0; for (int i = 1; i <= N; ++ i) { ll now = pre[i] - differ; if (now - min > max) differ += (now - min - max); now = pre[i] - differ; min = std::min(min, now); } return (differ <= rest); } ll Answer(int k) { int l = 1, r = 4e7 + 1, result = 0; while (l <= r) { int mid = (l + r) >> 1; if (check(mid, k)) { result = mid; r = mid - 1; } else { l = mid + 1; } } return result - 2e7; } int main() { std::ios::sync_with_stdio(false); std::cin.tie(0); std::cout.tie(0); std::cin >> N; for (int i = 1; i <= N; ++ i) { std::cin >> a[i]; pre[i] = pre[i - 1] + a[i]; } std::cin >> K; for (int i = 1; i <= K; ++ i) { answer += Answer(i); answer %= mod; } std::cout << answer << '\n'; return 0; }
暴力写法2
同样是二分,只不过改一下 。
我们先设 表示我们前缀和可以到的最大值。
然后若当前位置的前缀和大于 ,那么我们就减去大于的部分就行。
但是这里把减法变成加法了,我们将此时的 赋值成 (表示 到 的前缀和) 就行。
若我们之后一直保持现在的 ,那么意味着后半部分一直受修改之前的影响。
若 被更新成 ( 表示二分的区间最大和为 ) 了,那么就不受修改之前的影响了。
codeforces
#include <bits/stdc++.h> typedef long long ll; const int SIZE = 1100; const int mod = 998244353; int N, K; ll a[SIZE], pre[SIZE], answer; bool check(int max, int rest) { max -= 2e7; ll limit = max, has = 0; for (int i = 1; i <= N; ++ i) { if (pre[i] > limit) { has += pre[i] - limit; limit = pre[i]; } limit = std::min(limit, pre[i] + max); } return (has <= rest); } ll Answer(int k) { int l = 1, r = 4e7 + 1, result = 0; while (l <= r) { int mid = (l + r) >> 1; if (check(mid, k)) { result = mid; r = mid - 1; } else { l = mid + 1; } } return result - 2e7; } int main() { std::ios::sync_with_stdio(false); std::cin.tie(0); std::cout.tie(0); std::cin >> N; for (int i = 1; i <= N; ++ i) { std::cin >> a[i]; pre[i] = pre[i - 1] + a[i]; } std::cin >> K; for (int i = 1; i <= K; ++ i) { answer += Answer(i); answer %= mod; } std::cout << answer << '\n'; return 0; }
codeforces
#include <bits/stdc++.h> typedef long long ll; const int SIZE = 1100; const int mod = 998244353; int N, K; ll a[SIZE], pre[SIZE], answer; bool check(int max, int rest) { max -= 2e7; ll limit = max, has = 0; for (int i = 1; i <= N; ++ i) { if (pre[i] > limit) { has += pre[i] - limit; limit = pre[i]; } limit = std::min(limit, pre[i] + max); } return (has <= rest); } ll Answer(int k) { int l = 1, r = 4e7 + 1, result = 0; while (l <= r) { int mid = (l + r) >> 1; if (check(mid, k)) { result = mid; r = mid - 1; } else { l = mid + 1; } } return result - 2e7; } int main() { std::ios::sync_with_stdio(false); std::cin.tie(0); std::cout.tie(0); std::cin >> N; for (int i = 1; i <= N; ++ i) { std::cin >> a[i]; pre[i] = pre[i - 1] + a[i]; } std::cin >> K; for (int i = 1; i <= K; ++ i) { answer += Answer(i); answer %= mod; } std::cout << answer << '\n'; return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!