第十一届山东省大学生程序设计竞赛题解(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 n1条边的权值都是1,答案就是 n − 1 n-1 n1

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×(n1)

然后因为 n n n 比较大, n ≤ 1 0 5 n\\le 10^5 n105,所以我们考虑分情况讨论。

n n n 比较小,可以直接暴力 O ( n 2 ) O(n^2) O(n2) 连边然后求最小生成树即可。

n n n 较大,若 R − L + 1 R-L+1 RL+1 较大,则根据素数分布,int范围内的素数间距大概就是几百左右,这样 L ∼ L + ( R − L + 1 ) L\\sim L+(R-L+1) LL+(RL+1) 中一定会有素数,也就是说一定会出现一个 a i  is prime a_i\\ \\text{is\\ prime} ai is prime,答案显然就是上面讨论的 n − 1 n-1 n1

n n n 较大, R − L + 1 R-L+1 RL+1 较小,出题人可以找一个没有素数的区间 [ L , R ] [L,R] [L,R],那么答案还是 n − 1 n-1 n1 吗?显然仍然是,因为 n n n 较大, R − L + 1 R-L+1 RL+1 较小说明 a 1 ∼ a n a_1\\sim a_n a1an 一定会把 [ L , R ] [L,R] [L,R] 给取满了,也就是 a i a_i ai 是一堆连续的数,而相邻两个数互质,所以答案一定还是 n − 1 n-1 n1

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)=2k1 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)的主要内容,如果未能解决你的问题,请参考以下文章

第十一届山东省大学生程序设计竞赛

第十一届山东省大学生程序设计竞赛(正式赛)

山东省第一届acm程序设计竞赛题解

第十一届山东省大学生程序设计竞赛 L. Construction of 5G Base Stations(概率期望,递推前缀和优化)

第十六届全国大学生智能车竞赛线上赛点赛道审核 - 山东赛区

第十五届北京师范大学程序设计竞赛现场决赛题解