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

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

前言

A 珂学送分

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

B 遇见

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

C 位数差

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

D 蝴蝶

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

E 迷宫

链接

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

题意

给定一个n个点的树,以1为根,边权都是1。一个人从起点S开始随机游走(每一步等概率沿当前点的某一条边走),目的地是根节点。你可以选择k个点做标记,使得人在有标记的点时,他下一步一定向根节点方向的边走一步,并且再也不会回到有标记的点。在最优的标记方案下,人期望多少步能到根节点。
\(n\leq 100000,k\leq 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\n",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 取模的结果。
\(n\leq 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\n",ans);
    return 0;
}
posted @ 2021-02-11 14:49  Iscream-2001  阅读(68)  评论(0编辑  收藏  举报
/* */ /* */