第十一届山东省大学生程序设计竞赛题解(9 / 13)
Posted 繁凡さん
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第十一届山东省大学生程序设计竞赛题解(9 / 13)相关的知识,希望对你有一定的参考价值。
整理的算法模板合集: ACM模板
实际上是一个全新的精炼模板整合计划
VP了一下,体验不是太好,区分度不是很好,简单题太简单,难题看着就不想写…没什么意思
比赛地址:
https://ac.nowcoder.com/acm/contest/15600
%VP地址(密码 swpuacm):
%[https://ac.nowcoder.com/acm/contest/16646#description]https://ac.nowcoder.com/acm/contest/16646#description)
目录
第十一届山东省大学生程序设计竞赛(9 / 13)
B. Build Roads
Problem
给定一个 n n n 个点的无向完全图, i i i 和 j j j 之前的边权是 gcd ( a i , a j ) \\gcd(a_i,a_j) gcd(ai,aj) ,保证数组 a 随机,求 MST。
Solution
边权为 gcd ( a i , a j ) \\gcd(a_i, a_j) gcd(ai,aj),显然如果存在一个点 a x a_x ax 是素数,则从该点向所有的点连边,这样连接的 n − 1 n-1 n−1条边的权值都是1,答案就是 n − 1 n-1 n−1 。
若 L = R L=R L=R,则 R + L + 1 = 1 R+L+1=1 R+L+1=1,模1之后得到的显然都是0,也就是说 n 2 n^2 n2 条边的权值都是 L L L,答案显然就是 L × ( n − 1 ) L\\times (n-1) L×(n−1)。
然后因为 n n n 比较大, n ≤ 1 0 5 n\\le 10^5 n≤105,所以我们考虑分情况讨论。
若 n n n 比较小,可以直接暴力 O ( n 2 ) O(n^2) O(n2) 连边然后求最小生成树即可。
若 n n n 较大,若 R − L + 1 R-L+1 R−L+1 较大,则根据素数分布,int范围内的素数间距大概就是几百左右,这样 L ∼ L + ( R − L + 1 ) L\\sim L+(R-L+1) L∼L+(R−L+1) 中一定会有素数,也就是说一定会出现一个 a i is prime a_i\\ \\text{is\\ prime} ai is prime,答案显然就是上面讨论的 n − 1 n-1 n−1。
若 n n n 较大, R − L + 1 R-L+1 R−L+1 较小,出题人可以找一个没有素数的区间 [ L , R ] [L,R] [L,R],那么答案还是 n − 1 n-1 n−1 吗?显然仍然是,因为 n n n 较大, R − L + 1 R-L+1 R−L+1 较小说明 a 1 ∼ a n a_1\\sim a_n a1∼an 一定会把 [ L , R ] [L,R] [L,R] 给取满了,也就是 a i a_i ai 是一堆连续的数,而相邻两个数互质,所以答案一定还是 n − 1 n-1 n−1。
Code
#include <bits/stdc++.h>
using namespace std;
using ull = unsigned long long;
const int maxn = 2e5+5;
bool prime[maxn];
int n,L,R,a[maxn];
ull seed;
void getprime()
{
memset(prime,1,sizeof(prime));
prime[1] = 1;
for(int i = 2;i <= 200000;++i){
if(prime[i]){
for(int j = i+i;j <= 200000;j += i)prime[j] = 0;
}
}
}
ull xorshift64()
{
ull x = seed;
x ^= x<<13;
x ^= x>>7;
x ^= x<<17;
return seed=x;
}
int gen()
{
return xorshift64()%(R-L+1)+L;
}
struct Edge
{
int x,y,cost;
};
void solve1()
{
vector<Edge>edge;
for(int i = 1;i <= n;++i){
for(int j = i+1;j <= n;++j){
edge.push_back({i,j,__gcd(a[i],a[j])});
}
}
vector<int> pre(n+1);
for(int i = 1;i <= n;++i)pre[i] = i;
sort(edge.begin(),edge.end(),[](const Edge&a,const Edge&b)->bool{
return a.cost<b.cost;
});
function<int(int)> Find = [&](int x)->int{
return pre[x]==x?x:pre[x] = Find(pre[x]);
};
int ans = 0;
for(auto &x:edge){
int fx = Find(x.x);
int fy = Find(x.y);
if(fx!=fy){
pre[fx] = fy;
ans += x.cost;
}
}
printf("%d\\n",ans);
}
int main()
{
getprime();
scanf("%d%d%d%llu",&n,&L,&R,&seed);
for(int i = 1;i <= n;++i){
a[i] = gen();
}
for(int i = 1;i <= n;++i){
if(prime[a[i]]){
printf("%d\\n",n-1);
return 0;
}
}
if(n <= 3000){
solve1();
}
else if(L!=R){
printf("%d\\n",n-1);
}
else printf("%lld\\n",1ll*R*(n-1));
}
C. Cat Virus
Problem
给定一棵树,黑白染色方案,满足一个黑点的子树都是黑点,白点任意。
你现在构造一棵树,使得它的染色方案数为
K
K
K。
Solution
给定方案数让我们构造出一颗满足条件的树,我们先考虑对于一颗树,我们如何计算它的方案数。
我们设 f ( u ) f(u) f(u) 表示染色 u u u 以及 u u u 的所有子树的方案数,显然对于 u u u 的所有子节点 v v v,其中若将 u 染成黑色结点,则方案数为1(儿子全都是黑色结点),若将 u 染成 白色,则方案数为 ∏ f ( v ) \\prod f(v) ∏f(v),即: f ( u ) = ∏ ( f ( v ) + 1 ) + 1 f(u)=\\prod (f(v)+1)+1 f(u)=∏(f(v)+1)+1。
这里要求构造一棵染色方案数等于 k k k 的树。
对于 k k k:
若 k k k 是奇数,显然我们可以为 k k k 分配一个左右儿子 l , r l,r l,r ,我们继续往右儿子的方向往下走,这样 f ( r ) = k − 1 2 f(r)=\\cfrac{k-1}{2} f(r)=2k−1, f ( l ) = 2 , f ( u ) = f ( r ) × f ( l ) + 1 f(l)=2,f(u)=f(r)\\times f(l)+1 f(l)=2,f(u)=f(r)×f(l)+1。
若 k k k 是偶数,我们可以直接给 u 连接一个子节点,然后到下一层,方案数就变成了 k − 1 k-1 k−以上是关于第十一届山东省大学生程序设计竞赛题解(9 / 13)的主要内容,如果未能解决你的问题,请参考以下文章
第十一届山东省大学生程序设计竞赛 L. Construction of 5G Base Stations(概率期望,递推前缀和优化)