9.16题解

I AK IOI

这题把T3和T4放到了前面,所以建议阅读顺序 3-4-1-2。

T1

我把每个结论隔开了,看着应该会好一点。


我们知道,生成树就是给非根的每个结点找一个父亲。

如果 $x$ 的父亲是 $y$,那么它们之间的边权是 $lcm(x,y)≥x$。

我们知道,当 $y$ 是 $x$ 的因数时,$lcm(x,y)=x$,是最优情况

所以,最优情况应是:令每个点的父亲都是这个点的因数, 边权为 $x$。


但是我们很容易就会发现,质数的因数只有 $1$ 和自己。

肯定不能连自环,而且图中也没有点 $1$,需要另外考虑。

我们还知道,如果 $x$ 是质数,那么 $lcm(x,y)=xy$。

所以令 $y$ 最小即可(令 $y=2$),边权为 $2x$。


那么合数都连到因数上,因数为质数时连到 $2$ 上,

我们就可以得到:$2$ 是根节点


我们来梳理一下:对于 $3≤x≤n+1$

如果 $i$ 点为合数,那么 $i$ 的父亲为 $i$ 的任意因数,总边权加上 $i$。

如果 $i$ 点为质数,那么 $i$ 的父亲为 $2$,总边权加上 $2i$。


这个涉及到了一些质数筛的知识,我懒得写了,下面的程序用的是欧筛。

我们就可以写出程序:

#include <iostream>
#define int long long
using namespace std;
int pms[10000050], n, cnt, ans;bool vis[10000050];
signed main()
{
    cin >> n;
    for(int i = 2;i <= n + 1;++i)
    {
        if(!vis[i]) pms[++cnt] = i;
        for(int j = 1;i * pms[j] <= n + 1;++j)
        {
            vis[i * pms[j]] = 1;
            if(i % pms[j] == 0) break;
        }
    }
    //此时若vis[i]==1,则i为合数
    for(int i = 3;i <= n + 1;++i)
    {
        if(vis[i]) ans += i;
        else ans += 2 * i;
        ans %= 998244353;
    }
    cout << ans;
    return 0;
}

那就会有人问了:如果我不会数论呢?

这里建议宁打表找规律呢。(我考场上的做法

暴力程序大家都会写,我们来打下表:

n= 1 2 3 4 5 6 7 8 9 10 …… n
n+1= 2 3 4 5 6 7 8 9 10 11 …… n+1
ans= 0 6=0+3×2 10=6+4 20=10+5×2 26=20+6 40=26+7×2 48=40+8 57=48+9 67=57+10 89=67+2×11 …… ans

观察 $n+1$ 和 $ans$ 行加粗的数,你有什么发现?

分别观察 $ans$ 行有无 $×2$ 的数,你又有什么发现?

我们可以观察得到规律:

令 $i=3 \rightarrow n+1$,如果 $i$ 是合数 $ans+=i$,否则 $ans+=2i$。

于是我们愉快地切掉了这题。

T2

前置芝士:前缀和数组是啥。

令 $s[x]=\sum\limits_{i=1}^xa[i]$,则 $s$ 为 $a$ 的前缀和数组。

说人话:$s[x]=a[0]+a[1]+……+a[x]$。

那么 $s$ 的和就是:

$\ \ \ \ a[0]+(a[0]+a[1])+(a[0]+a[1]+a[2])+……+(a[0]+a[1]+……+a[n-1])$

$=n·a[0]+(n-1)·a[1]+(n-2)·a[2]+……+2·a[n-2]+1·a[n-1]$

我们可以发现,下标越靠前,占的比重越大

因为本来 $kkk$ 就会按照获得顺序拿牌,他越先拿到的牌占的比重越大,

所以只要有大牌,早给肯定比晚给更优

所以只要保证每次给的都是当前最大即可。

那就很好想了:每次把手里最大的给 $kkk$ 即可。

注意这题拿牌是按顺序的,不用考虑拿牌方案。

#include <iostream>
#include <queue>
#define int long long
using namespace std;
int n, k, ans;priority_queue<int> q;
signed main()
{
    cin >> n >> k;
    for(int i = 0, t;i < k;++i)
        cin >> t, q.push(t);
    for(int i = 0, t;i < n;++i)
        ans += (n - i) * q.top(), q.pop(), cin >> t, q.push(t);
    cout << ans;
    return 0;
}

T3

懒得写了,这里的T2就是

注意 long long 的使用,还有这题几乎每行都是细节。

T4

这里的T3就是

注意取件码头尾的判断。

posted @ 2021-09-17 09:17  5k_sync_closer  阅读(1)  评论(0编辑  收藏  举报  来源