牛客小白月赛99

牛客小白月赛99

\(A\) 牛客 NC275617 材料打印 \(AC\)

  • \(by+a \times \min(x,y)\) 即为所求。

    点击查看代码
    int main()
    {
        ll t,a,b,x,y,i;
        cin>>t;
        for(i=1;i<=t;i++)
        {
            cin>>a>>b>>x>>y;
            cout<<b*y+a*min(x,y)<<endl;
        }
        return 0;
    }
    

\(B\) 牛客 NC275619 %%% \(AC\)

  • 观察样例发现和 \(\log_{2}\) 相关,自然而然地想到每次减少一半左右。

  • 容易发现每次取 \(mod=\left\lfloor \frac{n}{2} \right\rfloor+1\) ,从而使 \(n\) 变成 \(\left\lfloor \frac{n}{2} \right\rfloor-1\) 是最优情况。

    • \(mod=\left\lfloor \frac{n}{2} \right\rfloor-1\) 不如 \(mod=\left\lfloor \frac{n}{2} \right\rfloor+1\) 优。
    点击查看代码
    int main()
    {
        ll t,n,ans,i;
        cin>>t;
        for(i=1;i<=t;i++)
        {
            cin>>n;
            ans=0;
            while(n!=0)
            {
                n%=(n/2+1);
                ans++;
            }
            cout<<ans<<endl;
        }
        return 0;
    }
    

\(C\) 牛客 NC275634 迷宫 \(AC\)

  • \(DFS\) 处理出起点、终点所在的连通块,分别记作 \(S,E\) 。若 \(E\) 中存在一个点或与其相邻的点可以被 \(S\) 中点使用一次超能力到达则合法。

    点击查看代码
    int hang[1010],lie[1010],vis[1010][1010],dx[5]={0,1,-1,0,0},dy[5]={0,0,0,1,-1},n,m;
    char s[1010][1010];
    void dfs1(int x,int y)
    {
        vis[x][y]=1;
        hang[x]=1;
        lie[y]=1;
        for(int i=1;i<=4;i++)
        {
            int nx=x+dx[i],ny=y+dy[i];
            if(1<=nx&&nx<=n&&1<=ny&&ny<=m&&vis[nx][ny]==0&&s[nx][ny]!='#')
            {
                dfs1(nx,ny);
            }
        }
    }
    void dfs2(int x,int y)
    {
        vis[x][y]=1;
        if(hang[x]==1||hang[x-1]==1||hang[x+1]==1||lie[y]==1||lie[y-1]==1||lie[y+1]==1)
        {
            cerr<<x<<" "<<y<<endl;
            cout<<"YES"<<endl;
            exit(0);
        }
        for(int i=1;i<=4;i++)
        {
            int nx=x+dx[i],ny=y+dy[i];
            if(1<=nx&&nx<=n&&1<=ny&&ny<=m&&vis[nx][ny]==0&&s[nx][ny]!='#')
            {
                dfs2(nx,ny);
            }
        }
    }
    int main()
    {
        int sx,sy,ex,ey,i,j;
        cin>>n>>m;
        for(i=1;i<=n;i++)
        {
            for(j=1;j<=m;j++)
            {
                cin>>s[i][j];
                if(s[i][j]=='S')
                {
                    sx=i;
                    sy=j;
                }
                if(s[i][j]=='E')
                {
                    ex=i;
                    ey=j;
                }
            }
        }
        dfs1(sx,sy);
        memset(vis,0,sizeof(vis));
        dfs2(ex,ey);
        cout<<"NO"<<endl;
        return 0;
    }
    

\(D\) 牛客 NC275683 又是一年毕业季 \(AC\)

  • 在前 \(n+1\) 个质数里找到一个最小的不在 \(\{ a \}\) 中出现过的质数即可。

    点击查看代码
    int prime[300010],vis[3000010],f[3000010],len=0;
    void isprime(int n)
    {
        memset(vis,0,sizeof(vis));
        for(int i=2;i<=n;i++)
        {
            if(vis[i]==0)
            {
                len++;
                prime[len]=i;	
            }
            for(int j=1;j<=len&&i*prime[j]<=n;j++)
            {
                vis[i*prime[j]]=1;
                if(i%prime[j]==0)
                {
                    break;
                }
            }
        }
    }
    int main()
    {
        int t,n,a,i,j;
        cin>>t;
        isprime(3000000);
        for(j=1;j<=t;j++)
        {
            memset(f,0,sizeof(f));
            cin>>n;
            for(i=1;i<=n;i++)
            {
                cin>>a;
                if(a<=3000000&&vis[a]==0)
                {
                    f[a]=1;
                }
            }
            for(i=1;i<=n+1;i++)
            {
                if(f[prime[i]]==0)
                {
                    cout<<prime[i]<<endl;
                    break;
                }
            }
        }
        return 0;
    }
    

\(E\) 牛客 NC275713 多米诺骨牌 \(AC\)

  • CF56E Domino Principle ,处理出向右的最远延伸距离。

  • 删掉被包含的小区间,然后取 \(m\) 个长度较大的彼此不相交区间即可。

    点击查看代码
    struct node
    {
        int x,r,L,R;
    }a[200010];
    int len[200010];
    bool cmp(node a,node b)
    {
        return a.x<b.x;
    }
    int main()
    {
        int t,n,m,nn,pos,ans=0,i,j;
        cin>>t;
        for(j=1;j<=t;j++)
        {
            cin>>n>>m;
            ans=nn=pos=0;
            for(i=1;i<=n;i++)
            {
                cin>>a[i].r;
            }
            for(i=1;i<=n;i++)
            {
                cin>>a[i].x;
            }
            sort(a+1,a+1+n,cmp);
            for(i=1;i<=n;i++)
            {
                a[i].L=a[i].R=i;
            }
            for(i=n;i>=1;i--)
            {
                while(a[i].R+1<=n&&a[i].x+a[i].r>=a[a[i].R+1].x)
                {
                    a[i].r=max(a[i].r,a[a[i].R+1].x+a[a[i].R+1].r-a[i].x);
                    a[i].R=a[a[i].R+1].R;
                }
            }
            for(i=n;i>=1;i--)
            {
                while(a[i].R+1<=n&&a[i].x+a[i].r>=a[a[i].R+1].x)
                {
                    a[i].R=max(a[i].R,a[a[i].R+1].R);
                }
            }
            for(i=1;i<=n;i++)
            {
                if(a[pos].R>=a[i].R)
                {
                    continue;
                }
                else
                {
                    nn++;
                    pos=i;
                    len[nn]=a[i].R-a[i].L+1;
                }
                
            }
            sort(len+1,len+1+nn,greater<int>());
            for(i=1;i<=min(nn,m);i++)
            {
                ans+=len[i];
            }
            cout<<ans<<endl;
        }
        return 0;
    }
    

\(F\) 牛客 NC275719 自爆机器人

  • 考虑在 \(i,j\) 两栋墙壁中移动的本质是消耗 \(2|x_{i}-x_{j}|\) 的时间。

  • 除去 \(n\) 的必须消耗时间外,以 \(m-1\) 段墙壁间的区间作为物品跑完全背包即可。

  • \(m-1\) 段的区间长度中至多有 \(\sqrt{n}\) 个本质不同的长度,去重后时间复杂度为单次查询时间复杂度为 \(O(t\sqrt{n})\)

    点击查看代码
    int x[200010],a[200010],f[200010];
    int main()
    {
        int T,n,m,t,ans,i,j,k;
        cin>>T;
        for(k=1;k<=T;k++)
        {
            cin>>n>>m>>t;
            ans=0;
            memset(f,0,sizeof(f));
            for(i=1;i<=m;i++)
            {
                cin>>x[i];
            }
            sort(x+1,x+1+m);
            for(i=1;i<=m-1;i++)
            {
                a[i]=2*(x[i+1]-x[i]);
            }
            sort(a+1,a+m);
            a[0]=unique(a+1,a+m)-(a+1);
            f[0]=1;
            for(i=1;i<=a[0];i++)
            {
                for(j=a[i];j<=t-n;j++)
                {
                    f[j]|=f[j-a[i]];
                }
            }
            for(i=t-n;i>=0;i--)
            {
                if(f[i]==1)
                {
                    ans=i+n;
                    break;
                }
            }
            cout<<ans<<endl;
        }
        return 0;
    }
    

\(G\) 牛客 NC275735 大鱼吃小鱼

  • 考虑进行差分,接着进行前缀和即可计算某一时刻鱼的重量的总和。

  • 考虑将重量进行离散化,树状数组维护原重量方便差分的询问。

  • 吃掉不超过自身当前重量的鱼本质上是一个近似斐波那契求和的过程,迭代上界大约在 \(43\) 次左右,时刻离散化后暴力进行迭代即可。

    点击查看代码
    struct node
    {
        ll pos,val,tim;
    }q[200010];
    ll b[100010],l[100010],r[100010],y[100010],cnt=0;
    void add(ll pos,ll val,ll tim)
    {
        cnt++;
        q[cnt].pos=pos;
        q[cnt].val=val;
        q[cnt].tim=tim;
    }
    bool cmp(node a,node b)
    {
        return a.tim<b.tim;
    }
    struct BIT
    {
        ll c[200010];
        ll lowbit(ll x)
        {
            return (x&(-x));
        }
        void add(ll n,ll x,ll val)
        {
            for(ll i=x;i<=n;i+=lowbit(i))
            {
                c[i]+=val;
            }
        }
        ll getsum(ll x)
        {
            ll ans=0;
            for(ll i=x;i>=1;i-=lowbit(i))
            {
                ans+=c[i];
            }
            return ans;
        }
    }T;
    int main()
    {
        ll t,n,x,pos,ans,sum,num,last,i,j,k,h;
        cin>>t;
        for(h=1;h<=t;h++)
        {
            cin>>n>>x;
            cnt=0;
            for(i=1;i<=n;i++)
            {
                cin>>l[i]>>r[i]>>y[i];
                b[i]=y[i];
            }
            sort(b+1,b+1+n);
            b[0]=unique(b+1,b+1+n)-(b+1);
            for(i=1;i<=n;i++)
            {
                pos=lower_bound(b+1,b+1+b[0],y[i])-b;
                add(pos,y[i],l[i]);
                add(pos,-y[i],r[i]);
            }
            sort(q+1,q+1+cnt,cmp);
            ans=x;
            for(i=1;i<=cnt;i=j+1)
            {
                j=i;
                while(q[j+1].tim==q[j].tim&&j+1<=cnt)
                {
                    j++;
                }
                for(k=i;k<=j;k++)
                {
                    T.add(b[0],q[k].pos,q[k].val);
                }
                sum=x;
                last=0;
                while(1)
                {
                    num=T.getsum(upper_bound(b+1,b+1+b[0],sum)-b-1);
                    if(num==last)
                    {
                        break;
                    }
                    else
                    {
                        sum+=num-last;
                        last=num;
                    }
                }
                ans=max(ans,sum);
            }
            cout<<ans<<endl;
        }
        return 0;
    }
    

总结

  • \(D\) 把调和级数的时间复杂度算错了,吃了 \(1\) 发罚时;因为清空不当,吃了 \(2\) 发罚时。
  • \(F\) 赛时没切可能是因为太着急了(?)。
posted @ 2024-08-24 09:15  hzoi_Shadow  阅读(40)  评论(0编辑  收藏  举报
扩大
缩小