2020 年百度之星·程序设计大赛 - 初赛一 题解

废话

虽然说是题解,但是并没有贴代码……

因为只写了部分的题,剩下的题因为种种原因 (太懒了) 没有写代码,于是想着全部都不贴好了qwq。

最后的排名是 59 59 59,在这里记录一下。

比赛传送门

T1 Drink

枚举每种饮料,求出只喝这种需要的数量,更新一下答案即可。

T2 GPA

枚举每一科成绩即可,可以注意到,对于同一个绩点,只需要枚举成绩为该绩点对应的最低分就够了。

T3 Dec

一开始以为是什么神奇的数论,写了发贪心交上去WA掉了嘤嘤嘤。

然后一看数据范围, a , b a,b a,b 只有 1000 1000 1000,于是直接写一发dp就可以了,令 f [ i ] [ j ] f[i][j] f[i][j] 表示 a = i , b = j a=i,b=j a=i,b=j 时的最优解,那么有: f [ i ] [ j ] = max ⁡ ( f [ i − 1 ] [ j ] , f [ i ] [ j − 1 ] ) + [ gcd ⁡ ( i , j ) = 1 ] f[i][j]=\max(f[i-1][j],f[i][j-1])+[\gcd(i,j)=1] f[i][j]=max(f[i1][j],f[i][j1])+[gcd(i,j)=1]

T4 Civilization

先枚举作为城市的点,可以算出起点到该点需要的时间。

产生新的人口时,肯定贪心地让它去附近 a [ i ] [ j ] a[i][j] a[i][j] 最大的点,就这样模拟一下即可。

T5 Rotate

由于 a i a_i ai 从外到内不减,所以对于第 i i i 层的黑段,不存在两个黑段同时与第 i + 1 i+1 i+1 层的黑段相连,所以这是一个森林的形态。

连通块数=点数-边数,特别的,第 n n n 层的贡献为 a [ n ] 2 \dfrac {a[n]} 2 2a[n],考虑第 i i i 层的贡献。

考虑第 i + 1 i+1 i+1 层的一个黑段与第 i i i 层的黑段相交的概率。对于第 i i i 层来说,一个黑段加一个白段为一个循环,长度为 2 a [ i ] \dfrac 2 {a[i]} a[i]2,考虑在这段内一个 i + 1 i+1 i+1 层的黑段与第 i i i 层的黑段不相交的概率,那么它能选择起点的区间长度为 1 a [ i ] − 1 a [ i + 1 ] \dfrac 1 {a[i]}-\dfrac 1 {a[i+1]} a[i]1a[i+1]1,除以总长度得到概率 a [ i + 1 ] − a [ i ] 2 a [ i + 1 ] \dfrac {a[i+1]-a[i]} {2a[i+1]} 2a[i+1]a[i+1]a[i],则相交的概率为 a [ i + 1 ] + a [ i ] 2 a [ i + 1 ] \dfrac {a[i+1]+a[i]} {2a[i+1]} 2a[i+1]a[i+1]+a[i],再乘上黑段数量 a [ i + 1 ] 2 \dfrac {a[i+1]} 2 2a[i+1] 得到总概率也是总边数期望 a [ i + 1 ] + a [ i ] 4 \dfrac {a[i+1]+a[i]} 4 4a[i+1]+a[i]

而第 i i i 层的贡献:点数 − - 边数就是 a [ i ] 2 − a [ i + 1 ] + a [ i ] 4 = a [ i ] − a [ i + 1 ] 4 \dfrac {a[i]} 2-\dfrac {a[i+1]+a[i]} 4=\dfrac {a[i]-a[i+1]} 4 2a[i]4a[i+1]+a[i]=4a[i]a[i+1]

所有层的贡献加起来就是答案,即 a [ 1 ] + a [ n ] 4 \dfrac {a[1]+a[n]} 4 4a[1]+a[n]

T6 Matrix

可以将点分为四类,即按照 c n t cnt cnt 的大小分类。

在每一类点中,再考虑其与原点的距离,那么每一类点可以分为若干层,每一层距离相同。

那么维护一个堆,堆内存每一类点离原点最近的那一层,然后每次取出距离 × c n t \times cnt ×cnt 最小的层贡献一下答案,然后将该类中的后一层加入堆中。

可以发现,对于一类点,统计其前 t t t 层的点数,大概是个与 t t t 相关的等差数列的公式,即包含 t 2 t^2 t2 这一项,所以增长速度很快,于是总时间复杂度只需要 O ( k ) O(\sqrt k) O(k )

细节比较多,做法也和官方题解有点差异,顺手贴下代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define ll long long

int T;ll a[10],k,ans,tot;
struct node{
    
    ll bel,dis,val;};
struct heap{
    
    
    node dui[10];int t;
    void init(){
    
    t=0;}
    void add(ll bel,ll dis,ll val){
    
    
        dui[++t]=(node){
    
    bel,dis,val};
        for(int i=t-1;i>=1;i--)
        if(dui[i+1].val<dui[i].val)swap(dui[i],dui[i+1]);
        else break;
    }
    node pop(){
    
    
        node re=dui[1];t--;
        for(int i=1;i<=t;i++)dui[i]=dui[i+1];
        return re;
    }
}dui;

int main()
{
    
    
    scanf("%d",&T);while(T--)
    {
    
    
        for(int i=1;i<=4;i++)scanf("%lld",&a[i]);scanf("%lld",&k);
        a[5]=1000000000000000000ll;dui.init();
        dui.add(1,0,0);dui.add(2,a[1]+1,(a[1]+1)*3);dui.add(3,a[2]+1,(a[2]+1)*2);dui.add(4,a[3]+1,a[3]+1);
        ans=tot=0;
        while(tot<k)
        {
    
    
            node x=dui.pop();
            ll i=x.bel,dis=x.dis,val=x.val,len;
            if(x.dis<=a[i])
            {
    
    
                if(i==1&&dis==0)tot++;
                else
                {
    
    
                    len=dis+1;
                    if(i>1&&dis-a[i-1]<=a[i-1])len-=a[i-1]-(dis-a[i-1])+1;
                    if(tot+4*len-4> k)ans+=(k-tot)*x.val,tot=k;
                    else tot+=4*len-4,ans+=(4*len-4)*x.val;
                }
            }
            else
            {
    
    
                len=a[i]-(dis-a[i])+1;
                if(i>1&&dis-a[i-1]<=a[i-1])len-=a[i-1]-(dis-a[i-1])+1;
                if(tot+4*len>k)ans+=(k-tot)*x.val,tot=k;
                else tot+=4*len,ans+=4*len*x.val;
            }
            x.dis++;if(x.dis>2*a[i])continue;
            x.val=x.dis*(4-x.bel+1);dui.add(x.bel,x.dis,x.val);
        }
        printf("%lld\n",ans);
    }
}

T7 Mosquito

可以比较容易发现一个网络流做法,先二分一个时间,然后以每个位置作为一个点,有窗户的位置与原点连边,流量为初始蚊子数,以及每个点都与汇点连边,流量为 1 1 1,最后窗户与在时间内能到达的点连边,流量无限。

但是由于总点数太大,会T的半分没有。

考虑到很多点的连边其实是相同的,我们可以将这些点合并起来,然后将他们的合并点与汇点连边,流量为这些点的点数。

具体来说,设 f [ i ] [ s t a ] f[i][sta] f[i][sta] ,其中 s t a sta sta 是一个二进制数表示状态,第 i i i 位为 1 1 1 表示在 i i i 时刻内能到达第 i i i 个窗户,然后 f [ i ] [ s t a ] f[i][sta] f[i][sta] 表示这样的点的数量,每次二分时 n m k nmk nmk 求出 f f f,然后建图泡网络流即可。

T8 Function

推柿子:
S ( n ) = ∑ i = 1 n ∑ j = 1 i j [ gcd ⁡ ( j , i j ) = 1 ] = ∑ i = 1 n i ∑ j = 1 ⌊ n i ⌋ [ gcd ⁡ ( i , j ) = 1 ] = ∑ k = 1 n μ ( k ) ∑ i = 1 ⌊ n k ⌋ i k ∑ j = 1 ⌊ n i k 2 ⌋ 1 = ∑ k = 1 n μ ( k ) ∑ i = 1 ⌊ n k ⌋ i k ⌊ n i k 2 ⌋ \begin{aligned} S(n)&=\sum_{i=1}^n\sum_{j=1}^i j [\gcd(j,\frac i j)=1]\\ &=\sum_{i=1}^n i\sum_{j=1}^{\lfloor \frac n i \rfloor} [\gcd(i,j)=1]\\ &=\sum_{k=1}^n\mu(k)\sum_{i=1}^{\lfloor \frac n k \rfloor} ik \sum_{j=1}^{\lfloor \frac n {ik^2} \rfloor}1\\ &=\sum_{k=1}^n\mu(k)\sum_{i=1}^{\lfloor \frac n k \rfloor} ik \lfloor \frac n {ik^2} \rfloor\\ \end{aligned} S(n)=i=1nj=1ij[gcd(j,ji)=1]=i=1nij=1in[gcd(i,j)=1]=k=1nμ(k)i=1knikj=1ik2n1=k=1nμ(k)i=1knikik2n

可以发现,当 k > n k>\sqrt n k>n i > ⌊ n k 2 ⌋ i>\lfloor \dfrac n {k^2} \rfloor i>k2n 时,后面的 ⌊ n i k 2 ⌋ \lfloor \dfrac n {ik^2} \rfloor ik2n 必然为 0 0 0,所以可以更改一下枚举上限:
= ∑ k = 1 n μ ( k ) k ∑ i = 1 ⌊ n k 2 ⌋ i ⌊ n i k 2 ⌋ \begin{aligned} &=\sum_{k=1}^{\sqrt n} \mu(k)k \sum_{i=1}^{\lfloor \frac n {k^2} \rfloor} i \lfloor \frac n {ik^2} \rfloor\\ \end{aligned} =k=1n μ(k)ki=1k2niik2n

G ( n ) = ∑ i = 1 n i ⌊ n i ⌋ G(n)=\sum_{i=1}^n i\lfloor \dfrac n i \rfloor G(n)=i=1niin,带入得:
= ∑ k = 1 n μ ( k ) k G ( ⌊ n k 2 ⌋ ) =\sum_{k=1}^{\sqrt n} \mu(k)k G(\lfloor \frac n {k^2} \rfloor) =k=1n μ(k)kG(k2n)

发现 G G G 是可以用整除分块求的,然后答案也可以用整除分块求,于是就做完了,时间复杂度为:
∑ i = 1 n n i 2 = n ∑ i = 1 n 1 i = n ln ⁡ n \sum_{i=1}^{\sqrt n} \sqrt{\frac n {i^2}}=\sqrt n\sum_{i=1}^{\sqrt n} \frac 1 i=\sqrt n\ln\sqrt n i=1n i2n =n i=1n i1=n lnn

猜你喜欢

转载自blog.csdn.net/a_forever_dream/article/details/107472511