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
注意 long long
的使用,还有这题几乎每行都是细节。
T4
注意取件码头尾的判断。