• 【日常摸鱼】牛客挑战赛3


    【日常摸鱼】牛客挑战赛3

    前言

    A 珂学送分

    期望dp,要用前缀和优化一下。。略了

    B 遇见

    比较水的追及问题。。。但是两个点一开始就在一起要特判掉。

    C 位数差

    稍微转化一下。。只用求数列中任意两对数的和的位数,这个可以随便套数据结构。

    D 蝴蝶

    思路大致跟上一场蝴蝶差不多。。而且比上场那个简单多了,连数据结构都不用套。。

    E 迷宫

    链接

    https://ac.nowcoder.com/acm/contest/20/E

    题意

    给定一个n个点的树,以1为根,边权都是1。一个人从起点S开始随机游走(每一步等概率沿当前点的某一条边走),目的地是根节点。你可以选择k个点做标记,使得人在有标记的点时,他下一步一定向根节点方向的边走一步,并且再也不会回到有标记的点。在最优的标记方案下,人期望多少步能到根节点。
    (nleq 100000,kleq 50)

    题解

    首先树上的随机游走的结论:从(i)(fa_i)的期望步数为(2size_i-1)。记(F_i) (=) (2size_i-1)
    然后如果没有标记的话,答案就是i到根节点所有的期望加起来。
    有标记的情况下,这些标记显然打在i到1的路径上。
    我们把这条路径从起点到根提取出来,这就变成一个序列(1号点是起点,最后一个点是根)上的DP问题。
    式子:(f_{i,k}) (=) (min(f_{j-1,k-1}+1+sum_i-sum_j-2sz_j(i-j)))
    (f_{i,k}) 表示前i个点放了k个标记的答案。
    (sum_i)表示前i个点的(F_i)之和。
    (sz_j)表示第i个点在原树中的(size)
    这个式子的形式明显可以斜率优化。

    (Code)

    #include <bits/stdc++.h>
    #define LL long long
    #define LD long double
    using namespace std;
    const int N=1e5+10;
    int read(){
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    void print(LL x){
        if(x>9) print(x/10);
        putchar(x%10+'0');
    }
    int n,m,K,St;
    vector<int> R[N];
    int fa[N];
    int sz[N];
    void dfs(int x){
        sz[x]=1;
        for(int i=0;i<R[x].size();++i){
            dfs(R[x][i]);
            sz[x]+=sz[R[x][i]];
        }
        return; 
    }
    
    LL a[N],b[N],s[N],c[N];
    LL f[55][N];
    int q[N];
    LD slope(int j,int k){
        return (LD)(c[k]-c[j])/(LD)(b[k]-b[j])/(LD)2;
    }
    int main(){
        int u,v;
        n=read();K=read();St=read();
        for(int i=2;i<=n;++i) {
            fa[i]=read();
            R[fa[i]].push_back(i);
        }
        if(St==1){
            puts("0");
            return 0;
        }
        dfs(1);
        m=0;
        while(St!=1){
            u=St;St=fa[St];++m;
            b[m]=sz[u];
            a[m]=(b[m]<<1)-1;
            s[m]=s[m-1]+a[m];
            f[0][m]=s[m];
        }
        n=m;
        int l,r;
        for(int k=1;k<=K;++k){
            for(int i=1;i<=n;++i)
                c[i]=f[k-1][i-1]-s[i]+(LL)2*i*b[i];
            l=1;r=0;
            for(int i=1;i<k;++i) {
                f[k][i]=f[k-1][i];
                while(l<r&&slope(q[r-1],q[r])>=slope(q[r],i)) --r;
                q[++r]=i;
            }
            for(int i=k;i<=n;++i){
                while(l<r&&slope(q[r-1],q[r])>=slope(q[r],i)) --r;
                q[++r]=i;
                while(l<r&&slope(q[l],q[l+1])<=(LD)i) ++l;
                f[k][i]=f[k-1][q[l]-1]+1+s[i]-s[q[l]]-(LL)2*(i-q[l])*b[q[l]];
            }
        }
        printf("%lld
    ",f[K][n]);
        return 0;
    }
    

    F 01序列

    链接

    https://ac.nowcoder.com/acm/contest/20/F

    题意

    给定长度为 n 的01序列,序列中部分位置已经确定,剩余部分01等概率出现。对最终序列求最长不下降序列(如有多种可能序列,则在此基础上最大化1的个数),设最长不下降序列的长度为 len,最长不下降序列中1的个数为 num,求期望 E(len * num) * 2 ^ 10000 对 1000000007 取模的结果。
    (nleq 1000)

    题解

    后面乘的2^10000没什么用,只是把前面E的分母都抵消了而已。。
    于是我们要算的就是所有情况的len*num的总和。。
    我们先考虑对已知序列,怎么求1最多的最长不下降子序列。。
    最终的子序列必然是一段0和一段1。。。
    我们从左往右依次考虑,假设当前序列的最优解是前面一段0,然后从第i个位置开始全选1。。
    如果后面再放个1,直接取过来就行。。
    如果后面放个0,如果从i开始的1的个数不少于0的个数,那么什么都不用做。
    如果放0后,从i开始的0的个数大于1的个数,那么这一段全选0的长度一定优于全选1。于是这部分的1全部舍弃掉。
    这个过程我们很容易能用dp来维护。。于是这题就做完了。

    (Code)

    #include<bits/stdc++.h>
    #define LL long long
    using namespace std;
    const int INF=1e9;
    const int N=1e3+100;
    const LL P=1000000007;
    int read(){
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    void print(LL x){
        if(x>9) print(x/10);
        putchar(x%10+'0');
    }
    void pls(LL &x,LL y){
        x+=y;if(x>=P)x-=P;
    }
    int n;
    int a[N];
    LL len[N][N],num[N][N],cnt[N][N],dp[N][N];
    int main(){
        int tt=10000;
        n=read();
        for(int i=1;i<=n;++i) a[i]=read();
        cnt[0][0]=1;
        for(int i=0;i<n;++i){
            if(a[i+1]==-1) --tt;
            for(int j=0;j<=i;++j){
                if(a[i+1]!=1){
                    if(j>0) {
                        pls(len[i+1][j-1],len[i][j]);
                        pls(cnt[i+1][j-1],cnt[i][j]);
                        pls(num[i+1][j-1],num[i][j]);
                        pls(dp[i+1][j-1],dp[i][j]);
                    }
                    else{
                        pls(len[i+1][j],(cnt[i][j]+len[i][j])%P);
                        pls(cnt[i+1][j],cnt[i][j]);
                    }
                }
                if(a[i+1]!=0){
                    pls(len[i+1][j+1],(cnt[i][j]+len[i][j])%P);
                    pls(cnt[i+1][j+1],cnt[i][j]);
                    pls(num[i+1][j+1],(num[i][j]+cnt[i][j])%P);
                    pls(dp[i+1][j+1],(dp[i][j]+num[i][j]+len[i][j]+cnt[i][j])%P);
                }
            }
        }
        LL ans=0;
        for(int i=0;i<=n;++i) pls(ans,dp[n][i]);
        ans=(ans%P+P)%P;
        for(int i=1;i<=tt;++i) pls(ans,ans);
        printf("%lld
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    vue导航守卫和axios拦截器的区别
    js中的深拷贝与浅拷贝
    Storyboard中拖拽控件不能运行的问题(在运行的时候,相应的控件代码没有被执行)
    关于stringWithFormat:
    两层嵌套的JSON包的解法
    button的action属性如果有参数,必须加“:”
    iOS 协同开发出fatal error: file 'XX-Prefix.pch' has been modified since the precompiled header was built
    [转] Objective-C语法快速参考
    iOS应用程序内存查看工具
    XCode快捷键大全
  • 原文地址:https://www.cnblogs.com/Yuigahama/p/14397740.html
Copyright © 2020-2023  润新知