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

CSP-S 24

9.21

今天开始集训告一段落了,去补文化课一周。

如果写不完回来会补(?)

115=100+0+15

t1

先等会

t2

先等会

t3

9.22:回来补债了

\([\gcd(i,j)=1]=[\gcd(p_i,p_j)=1]\)

这个限制初看好像很难转化,只能猜到与组合数和素数有关。

于是我们从原始情况入手,考虑如何变换之后得到的排列是符合条件的。

发现对于 2,4,8,16 \(\cdots\) 这些数任意交换位置后得到排列都是合法的,考虑从此入手。

进一步手模数据,发现关系一:

对于所有质因数集合相同的数,任意交换位置后排列合法

解释:我们只关心两个数及其下标是否互质,因此我们只关心其质因数集合是否相同。(多模会数据就理解了)

可是这还不够,继续寻找。

关系二:

\(1\le i \le n\) 中,对于所有满足 $\lfloor \frac{n}{pri_i} \rfloor\le 1 $ 的素数可以与 \(1\) 交换位置

解释:\(1\) 永远与所有数互质,而对于 $\lfloor \frac{n}{pri_i} \rfloor\le 1 $ 的素数在 \(1\le i \le n\) 范围内也与所有数互质,与 \(1\) 等效,故交换后合法。

此时已经可以过掉 \(n=13\) 了,但当 \(n=14\) 时答案依旧错误。

发现错误原因:5,10 与 7,14 可以整体交换!

我们发现它们的质因数的出现次数相同 ,即说明对于一个质因数的约束,我们只关心其约束条件是否成立而并不关心质因数的值,因此对于质因数出现次数相同也可整体交换

此为关系三。

到此结论已推完,实现即可。

交换对应排列,乘对应阶乘即可。

只需找出所有质因数集合并统计个数 \(\cdots\)

等等,这一步咋实现 ?

map 里放 vector

这个我也是赛后才知道可以这么套,学到了。

具体实现看代码。

code

点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 10;
const int p = 13131;
const int mod = 1e9 + 7;
int n, ans;
int f[N], cnt[N];
int pri[N], tot;
int t[N];
bool flag[N];
vector<int> e[N];
map<vector<int>, int> mp;inline void init()
{f[0] = 1, f[1] = 1;flag[1] = 1;for (int i = 2; i <= n; ++i){f[i] = f[i - 1] * i % mod;if (!flag[i])pri[++tot] = i;for (int j = 1; j <= tot; ++j){if (i * pri[j] > n)break;flag[i * pri[j]] = 1;if (i % pri[j] == 0)break;}}
}signed main()
{freopen("tg.in", "r", stdin);freopen("tg.out", "w", stdout);ios::sync_with_stdio(0);cin.tie(0);cin >> n;init();for (int j = 1; j <= tot; ++j)for (int i = pri[j]; i <= n; i += pri[j]){e[i].push_back(j);++cnt[j];}for (int i = 1; i <= n; ++i){if (!flag[i]){if (e[i].size() == 1 && cnt[e[i][0]] == 1)e[i][0] = tot + 1;//将关系二中满足条件的素数与1放入一个集合}if (i == 1)e[i].push_back(tot + 1);++mp[e[i]];}ans = 1;for (int i = 1; i <= tot; ++i)++t[cnt[i]];for (int i = 2; i <= n; ++i) // 5 10 7 14 可互换:质因数出现次数相同时可互换,桶记录ans = ans * f[t[i]] % mod;for (auto y : mp) // 记录质因数种类相同的数的个数ans = ans * f[y.second] % mod;cout << ans << "\n";return 0;
}

t4

9.23:补ing

看完题第一感觉就是最小生成树,但发现将所有边连出再生成显然要爆炸,而只对每个城市内的图做最小生成树又不好处理高铁的情况。

这时我们要开始利用最小生成树的思想。

肯定要用到最小生成树,再做一部分后续处理。

对于每个城市内的图是同构的,显然可以做一遍最小生成树,先筛选出可能被选的边,再进行后续处理。

我们把枢纽站叫作关键点。

若无高铁站的情况,则显然为每个城市内最小生成树。

考虑有高铁站后的影响:

对于城市内的点,不一定可以在城市内直接到达,也就是说,若建造高铁站更优,则可以通过高铁站到达其它城市,再到达目标点,即在同一城市内,图不一定连通

考虑如何选择这些边:

观察易得,所有不在关键点之间路径上的树边都一定会被选,为了保证整张图连通,这些边必选,直接累加答案即可。

对于其它边,将其记录下来( \(e\) 数组)当作备选。

高铁站更优当且仅当 \(a_i\le b_i+d_j\) ,即 \(a_i-b_i\le d_j\)

也就是说,对于一个城市,所有边权(被选出的边)大于 \(a_i-b_i\) 的边,都应不修建。

这启发我们对于城市按 \(a_i\) 升序排列(后有解释),对于 \(e\) 数组按照边权升序进行排序,求前缀和,随后对于每个城市,二分找出哪一条边以后都应不修建,利用前缀和求出应修建的边的贡献,不修建的边数乘 \(a_i\) 作为修高铁的贡献。

对于 \(1\) 号城市与 \(l\) 号城市直接连权值最小的前 \(r-1\) 条边保证在最后一个城市内部连通,否则若没有城市内部连通,则高铁再多图也不连通。(这时体现将城市排序的作用,修建高铁时选择费用少的,对于费用最大的城市令其内部连通,以此在保证图连通时总费用最小)。

关于如何选出关键点之间的路径:

我们将关键点的 \(fa\) 都设为 \(0\) ,表示它们已被连通,之后正常最小生成树,若 \(find(u)\not=find(v)\) ,则不为关键点路径上的边,直接累加答案即可,否则表示为关键点路径上的点(因为所有关键点已事先“被连通”,发现两点的 \(fa\) 相同则一定为关键点加入树中路径上的点,不理解就造样例手模),将其加入 \(e\) 数组即可。

code

点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10;
int n, m, l, r, sumb, ans;
int fa[N], id[N];
int a[N], b[N], sumq[N];
int s[N], q[N], tot;
struct node
{int u, v;int val;
} e[N];
inline int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }
inline bool cmp(node a, node b) { return a.val < b.val; }
inline bool cmp2(int x, int y) { return a[x] < a[y]; }signed main()
{freopen("rb.in", "r", stdin);freopen("rb.out", "w", stdout);ios::sync_with_stdio(0);cin.tie(0);cin >> n >> m;for (int i = 1; i <= m; ++i){cin >> e[i].u >> e[i].v >> e[i].val;++e[i].u, ++e[i].v;}sort(e + 1, e + 1 + m, cmp);for (int i = 1; i <= n; ++i)fa[i] = i;for (int i = 1; i <= m; ++i) // 先最小生成树降低边数至n{int fu = find(e[i].u), fv = find(e[i].v);if (fu != fv)e[++tot] = e[i], fa[fu] = fv;}for (int i = 1; i <= n; ++i)fa[i] = i;cin >> l;for (int i = 1; i <= l; ++i)cin >> a[i] >> b[i], sumb += b[i];cin >> r;for (int i = 1; i <= r; ++i)cin >> s[i], ++s[i], fa[s[i]] = 0;tot = 0;for (int i = 1; i < n; ++i){int u = find(e[i].u), v = find(e[i].v);if (u != v)fa[u] = v, ans += sumb + e[i].val * l;elseq[++tot] = e[i].val;}for (int i = 1; i < r; ++i)sumq[i] = sumq[i - 1] + q[i];for (int i = 1; i <= l; ++i) // 城市之间fa[i] = id[i] = i;sort(id + 1, id + 1 + l, cmp2);for (int i = 1; i < l; ++i) // 连高铁{int x = id[i];int y = x % l + 1;x = find(x), y = find(y);if (b[x] < b[y])swap(x, y);int pos = upper_bound(q + 1, q + r, a[id[i]] - b[x]) - q - 1; // a b+d --> a-b d 二分找位置ans += b[x] * pos + sumq[pos] + (r - pos) * a[id[i]];fa[x] = y;}ans += b[find(1)] * (r - 1) + sumq[r - 1]; // a最大的直接连权值最小r-1条边保证连通cout << ans << "\n";return 0;
}

咕...

http://www.hskmm.com/?act=detail&tid=35311

相关文章:

  • 读书笔记:深入理解java虚拟机
  • CSP-S 19
  • CSP-S 20
  • Flutter应用设置插件 - 轻松打开iOS和Android系统设置
  • CSP-S 22
  • Project. 2025.11化学小组pre
  • MySQLDay1
  • 蛋白表达标签:重组蛋白研究的精妙引擎
  • 106.腾讯地图位置服务再出错
  • 心理咨询系统
  • Adaptive Learning Rate(自适应学习率) - -一叶知秋
  • Luogu P10034 「Cfz Round 3」Circle 题解 [ 蓝 ] [ 背包 DP ] [ 质数筛 ] [ 图论 ] [ 构造 ]
  • 2025.10.20模拟赛
  • SQLite简单使用
  • 新学期每日总结(第12天)
  • 2025.10.20总结 - A
  • CF2107E Ain and Apple Tree
  • 傻瓜式处理kauditd0病毒程序记录
  • win10 升级 win11 后时间更新失败
  • 2025,为什么公众号编辑器排版决定阅读完成率?——一次从流程到结果的深评
  • 软件工程学习日志2025.10.20
  • P14254 分割(树上计数问题) 题解
  • P14262 [ROI 2015 Day1] 自动好友
  • 软件工程第二次团队作业
  • 超越技术范畴:低代码如何重塑企业数字文化
  • 歌手与模特儿
  • 20251019
  • 十六天
  • 计算机毕业设计 基于EChants的海洋气象数据可视化平台设计与建立 Python 大数据毕业设计 Hadoop毕业设计选题【附源码+文档报告+安装调试】
  • https://www.luogu.com.cn/problem/CF1635E