当前位置: 首页 > news >正文

CSP-S 40(爆零记)

10.27

赤到了。

第一次爆蛋。

t1

特判没卡掉11个人。

乐死了。

暴力有80pts。

正解:

发现值域很小只有1000,从此入手。

先预处理 1000 以内的素数,发现很少只有168个,空间可开下,这启发我们对于每个素数记录东西。

开vector \(d\) 存每个位置上的数分解质因数后含有的素数下标

限制了 \(i\le j\) ,于是我们倒序处理,用后面的信息更新之前的信息。

数组 \(dd_{i,j}\) 表示当前含有第 \(i\) 个素数的位置所能到达的最小的含有第 \(j\) 个素数的位置。

数组 \(idd_{i,j}\) 表示当前\(i\) 个位置所能到达的最小的含有第 \(j\) 个素数的位置。

两者相互转移即可。

注意一大堆特判。

特别的:因为 \(\gcd(0,x)=x\) ,所以 \(0\) 可做任意两点间的中转点。

记数组 \(spe_i\) 表示由于 \(0\) 的存在,第 \(i\) 位置可到达的最近距离(以后也均可达)。

同时转移即可。

还是注意特判!!!

code

可恶的0与1
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
const int M = 170;
bool flag[N];
int pri[M], cnt;
int n, q;
vector<int> d[N];
int a[N];
int id_d[N][M], dd[M][M], spe[N];inline void xxs()
{for (int i = 2; i <= 1000; ++i){if (!flag[i])pri[++cnt] = i;for (int j = 1; j <= cnt; ++j){if (i * pri[j] > 1000)break;flag[i * pri[j]] = 1;if (i % pri[j] == 0)break;}}
}signed main()
{freopen("gcd.in", "r", stdin);freopen("gcd.out", "w", stdout);ios::sync_with_stdio(0);cin.tie(0);xxs();cin >> n >> q;for (int i = 1; i <= n; ++i){cin >> a[i];for (int j = 1; j <= cnt; ++j){if (a[i] % pri[j] == 0)d[i].emplace_back(j);if (pri[j] > a[i])break;}}memset(id_d, 0x3f, sizeof(id_d));memset(dd, 0x3f, sizeof(dd));memset(spe, 0x3f, sizeof(spe));for (int i = n; i; --i){if (!a[i]){spe[i] = i;continue;}spe[i] = spe[i + 1]; // 0及其之后都合法for (auto j : d[i]){for (int k = 1; k <= cnt; ++k)id_d[i][k] = min(id_d[i][k], dd[j][k]);}for (auto j : d[i])dd[j][j] = i, id_d[i][j] = i;for (auto j : d[i]){for (int k = 1; k <= cnt; ++k)dd[j][k] = min(dd[j][k], id_d[i][k]);}}int x, y;while (q--){cin >> x >> y;if (x == y)puts("Shi");else if (a[x] == 1 || a[y] == 1)puts("Fou");else if (!a[x] && !a[y]){for (int i = x + 1; i < y; ++i)if (a[i] > 1){puts("Shi");goto con;}puts("Fou");}else if (y >= spe[x])puts("Shi");else{for (auto i : d[y]){if (id_d[x][i] <= y){puts("Shi");goto con;}}puts("Fou");}con:;}return 0;
}

t2

构造题。

其实不难,赛时被 t1 恶心到了,看到 t2 脑子根本就是全是模拟。

反正就是爆了。

正解:

容易发现栈的数量至多为 2(两个栈一定可满足要求)。

尝试先判断栈的数量,发现不好弄。

不妨假设只有一个栈,每次以最优策略转移,若最终栈非空则需要两个栈。

一个栈很好做了,不多赘述。

加入另一个栈如何做?

我们可以将第二个栈当成中转站,对于每一个要取出的元素,将该元素上方的所有元素放到第二个栈中,之后取出该元素,再将刚才取出的元素放回。

发现唯一麻烦的就是求操作次数,设当前要拿出的元素的位置是 \(p\) ,次数 显然为 \(p-1-该元素上方的小于该元素值的元素个数\)(因为该元素上方的小于该元素值的元素个数在此之前已经被弹出了)。

这东西就是一个区间查询和单点加,拿个数据结构维护以下就行(线段树,树状数组什么的)。

code

具体细节看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int T, n;
int a[N], b[N];
int stk[N], top, topb, tot;
struct node
{int opt, i, j, cnt;
} ans[N << 2];
int c[N];
vector<int> num[N];#define lowbit(x) (x & (-x))inline void add(int pos, int val)
{for (int i = pos; i <= n; i += lowbit(i))c[i] += val;
}inline int query(int pos)
{int ans = 0;for (int i = pos; i; i -= lowbit(i))ans += c[i];return ans;
}inline void write(int x)
{cout << x << "\n";cout << tot << "\n";for (int i = 1; i <= tot; ++i){if (ans[i].opt == 1)cout << ans[i].opt << ' ' << ans[i].i << ' ' << ans[i].cnt << "\n";if (ans[i].opt == 2)cout << ans[i].opt << ' ' << ans[i].i << "\n";if (ans[i].opt == 3)cout << ans[i].opt << ' ' << ans[i].i << ' ' << ans[i].j << ' ' << ans[i].cnt << "\n";}
}inline void solve()
{cin >> n;for (int i = 1; i <= n; ++i)cin >> a[i], b[i] = a[i];sort(b + 1, b + 1 + n);stk[0] = 0;tot = top = 0;topb = 1;for (int id = n; id; --id){if (a[id] == b[topb]){// cerr << "id=" << id << "\n";ans[++tot] = {1, 1, 114514, 1};ans[++tot] = {2, 1, 114514, 1};++topb;while (top){if (stk[top] != b[topb])break;// cerr << "top=" << top << "\n";ans[++tot] = {2, 1, 114514, 1};--top, ++topb;}}else{ans[++tot] = {1, 1, 114514, 1};stk[++top] = a[id];}}if (!top){write(1);return;}else{vector<int> now;reverse(stk + 1, stk + top + 1);// cerr << "top=" << top << "\n";for (int i = 1; i <= n; ++i)c[i] = 0;for (int i = 1; i <= top; ++i)num[stk[i]].emplace_back(i), now.emplace_back(stk[i]);now.emplace_back(1e9);sort(now.begin(), now.end());for (int i = 1; i < now.size(); ++i)if (now[i - 1] != now[i])for (auto p : num[now[i - 1]]){int dx = p - 1 - query(p - 1);if (dx)ans[++tot] = {3, 1, 2, dx};ans[++tot] = {2, 1, 0, 0};if (dx)ans[++tot] = {3, 2, 1, dx};add(p, 1);// cerr << "i=" << i << "\n";}// cerr << "\n";for (int i = 1; i <= top; ++i)num[stk[i]].clear();write(2);}
}signed main()
{freopen("sort.in", "r", stdin);freopen("sort.out", "w", stdout);ios::sync_with_stdio(0);cin.tie(0);cin >> T;while (T--)solve();return 0;
}

t3

不是你赛场高精???

破防了。

t4

神秘dp。

真不是每个包拆开都成一道题了?

直接看题解吧:

对于每个点,称距离其最近的染色点为配对染色点。

观察到:对于子树 \(u\) 的所有点,其配对染色点至多只有1个在子树外(若有多个可以只留距离 \(u\) 点最近的)。

设计动态规划 \(dp[u][j]\) 代表搞定 \(u\) 子树、且 \(u\)\(j\) 配对的最小花费,记 \(f[i]=\min(dp[i][j])\),答案为 \(f[1]\)

考虑转移,对于 \(dist(i, j)\leq d[i]\) 的状态,考虑 \(u\) 的每个儿子 \(v\)

  • \(v\) 子树的配对集合完全在子树内,贡献为 \(f[v]\)
  • \(v\) 子树的配对集合除了 \(j\) 点都在子树内,贡献为 \(f[v]-c[j]\)
  • \(v\) 子树的配对集合有在子树外、且不是 \(j\) 的其他点 \(j'\),则不是最优解(子树外多余1个)

转移方程为 \(d p[u][j]=c[j]+\sum \min (f[v], dp[v][j]-c[j])\),复杂度 \(O(tn^2)\)

http://icebutterfly214.com/news/411/

相关文章:

  • 学校协同云盘怎么选?2025年10大热门教育网盘推荐与对比
  • GPU集群之间的交互
  • CF1267G Game Relics
  • 102302115方朴第一次作业
  • 解题报告-梦熊 CSP-S2025 模拟赛T2
  • 鄙“站”麻将和算24,刷新后会换
  • 20232422 2025-2026-1 《网络与系统攻防技术》实验三实验报告
  • 20232404 2025-2026-1 《网络与系统攻防技术》实验三实验报告
  • 「WC2014-紫荆花之恋」题解
  • 谢谢你周医生
  • 来源未知
  • Date 10.27
  • 10.27及动手动脑
  • go包装bing搜索
  • 鼾声识别芯片方案和睡眠产品的应用场景
  • 2025年工程管理软件公司综合推荐榜:助力建筑行业数字化升级
  • Excel高性能异步导出完整方案!
  • 2025年多功能综合杆厂家排名前十推荐
  • 2025年度在线网站客服系统综合排行榜正式发布
  • JDD Oxygen智能零售论坛 | 《大模型时代的广告营销变革与实践》
  • 2025年市面上新加坡留学品牌、行业内公司及口碑产品推荐排行
  • 11-文件上传
  • TensorFlow与PyTorch深度对比分析:从基础原理到实战选择的完整指南 - 指南
  • Navicat 17 超详细保姆级下载安装教程:附激活工具使用步骤​
  • el-date-picker样式修改
  • 浅谈 Agent 开发工具链演进历程
  • 电梯调度算法结对编程作业
  • 2025质量可靠的义乌刺绣工厂推荐榜
  • DP1312多协议高性能读卡芯片支持A/B/Felaca/18092智能门锁读卡器模拟卡兼容PN512 - 动能世纪
  • 2025年10月兰花油品牌推荐榜单:多维度深度对比与选择指南