ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

考后总结——8.4 暑假模拟16

2022-08-05 15:33:03  阅读:151  来源: 互联网

标签:8.4 16 int res sum dep maxn now 考后


概述

又名:来自学长的告别
估分:\(???+???+40+20=???\)
实际挂分:\(0+10+20+50=90\)
rk 19

赛时

干了快两小时 T1,以为是最可做,结果赛后发现 T1 人均最低分。
T2 最后感觉像是二分,但是判断写的是关于圆位置关系的函数,假掉了。
T3 和 T4 没什么思路,打了暴力跑路。

反思

和暴力拍过了,可能暴力的思路也是假的。
打暴力要快准稳,优化能多少就多少(在保证正确情况下),或许得分高于部分分。

题解

T1 进攻

题意

对于一棵 \(n\) 个节点的树,每个点的父亲在比自己编号小的点中随机选取,求树最大深度期望。

思路

不难设置 \(f(i,j)\) 为 \(i\) 个节点,最大深度为 \(j\) 的方案数,答案为:

\[\dfrac{\sum_{i=1}^n f(n,i)}{(n-1)!} \]

然后玄学转移,发现 \(2\) 号节点的父亲唯一,可以分类讨论这个最大深度是否来自 \(2\) 子树。
于是有转移方程:

\[f(i,j)=\sum_{x=1}^{i-1}\sum_{y=1}^{j} f(x,j-1)\times f(i-x,y)\times \dbinom{i-2}{x-1}+\sum_{x=1}^{i-1}\sum_{y=1}^{j-2} f(x,y)\times f(i-x,j)\times \dbinom{i-2}{x-1} \]

最后一维可以前缀和优化掉,复杂度 \(O(n^3)\)

代码

点击查看代码
int n,mod;
inline ll q_pow(ll x,int p){
    ll res=1;
    while(p){
        if(p&1) res=res*x%mod;
        x=x*x%mod;
        p>>=1;
    }
    return res;
}
ll C[405][405],fac;
ll f[405][405],sum[405][405];
ll ans;
int main(){
    n=read(),mod=read();
    C[0][0]=1,C[1][0]=1,C[1][1]=1;
    for(int i=2;i<=n;++i){
        C[i][0]=1,C[i][i]=1;
        for(int j=1;j<i;++j){
            C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
        }
    }
    fac=1;
    for(int i=1;i<n;++i) fac=fac*i%mod;
    f[1][1]=1,f[2][2]=1;
    sum[1][1]=1,sum[2][2]=1;
    for(int i=1;i<=2;++i){
        for(int j=i+1;j<=n;++j) sum[i][j]+=sum[i][j-1];
    }
    //i个点,最大深度为j的方案数
    for(int i=3;i<=n;++i){
        for(int j=2;j<=i;++j){
            //枚举子树2的大小
            //printf("%d %d\n",i,j);
            for(int x=1;x<=i-1;++x){
                //printf("%d\n",x);
                //答案出现在2子树,2子树深度为j-1,其余任意
                f[i][j]=(f[i][j]+f[x][j-1]*sum[i-x][j]%mod*C[i-2][x-1]%mod)%mod;
                //printf("1** %d %d %d %d\n",f[i][j],f[x][j-1],sum[i-x][j],C[i-2][x-1]);
                //答案出现在另外子树
                f[i][j]=(f[i][j]+sum[x][j-2]*f[i-x][j]%mod*C[i-2][x-1]%mod)%mod;
                //printf("2** %d %d %d %d\n",f[i][j],sum[x][j-2],f[i-x][j],C[i-2][x-1]);   
            }
            //printf("f-> %d\n",f[i][j]);
            sum[i][j]=f[i][j];
        } 
        for(int j=1;j<=n;++j) sum[i][j]=(sum[i][j]+sum[i][j-1])%mod;
    }
    for(int i=1;i<=n;++i){
        ans=(ans+f[n][i]*i%mod)%mod;
    }
    ans=ans*q_pow(fac,mod-2)%mod;
    printf("%lld\n",ans);
    return 0;
}

T2 Star Way To Heaven

题意

在一个 \(n\times m\) 的矩形(\(n\) 为横向距离)上,从左边界任意一点出发到右边界一点停止为一条路径。
矩形中有 \(k\) 个标记点,求路径上距离所有标记点以及上下边界的最小距离的最大值。

思路

考虑二分,已然钦定最小距离为 \(x\),则上下界内部 \(x\) 个单位长的区域以及每个标记点半径为 \(x\) 的圆都是不能去到的,只需判断这些点是否将矩形拦断,判断是 \(O(n^2)\) 的,会超时。
其实,最小距离最大值也可以使用最小生成树,找到生成树最大的边,满足条件的情况应为这条边对应两圆相切,于是这条边长度一半即为答案。

代码

点击查看代码
int n,m,k;
int x[6005],y[6005];
db dis[6005];
bool vis[6005];
inline db get_dis(int id1,int id2){
    db sqx=(db)(x[id1]-x[id2])*(x[id1]-x[id2]),sqy=(db)(y[id1]-y[id2])*(y[id1]-y[id2]);
    return sqrt(sqx+sqy);
}
db ans;
int main(){
    n=read(),m=read(),k=read();
    dis[0]=1e12;
    for(int i=1;i<=k;++i){
        x[i]=read(),y[i]=read();
        dis[i]=y[i];
    }
    dis[k+1]=(db)m;
    while(1){
        int id=0;
        for(int i=1;i<=k+1;++i){
            if(!vis[i]&&dis[i]<dis[id]) id=i;
        }
        vis[id]=1;
        ans=max(ans,dis[id]);
        if(id==k+1) return printf("%.10Lf\n",ans/2),0;
        for(int i=1;i<=k;++i){
            dis[i]=min(dis[i],get_dis(i,id));
        }
        dis[k+1]=min(dis[k+1],(db)(m-y[id]));
    }   
    return 0;
}

T3 God Knows

题意

二分图上,左边第 \(i\) 个点连向右边第 \(p_i\) 个点(保证 \(p_i\) 两两不同),每次可以在左侧删除一个未被删除的点,同时删除其连边以及与其连边相交的所有边对应的点。
删除左侧每个点都有一个代价,求全部删除的最小代价。

思路

发现两个删除操作“独立”当且仅当,\(i<j,p_i<p_j\) 且不存在 \(i<k<j,p_i<p_k<p_j\),于是本题是在求带权的极长上升子序列。
考虑 \(O(n^2)\) 的 dp,可以暴力转移,发现决策点的 \(p\) 单调递减,本质是一个单调栈,可以用线段树来维护,具体看这道例题,区别在于该题是前缀最大值,本题是后缀。对 \(p\) 建权值线段树,对应的维护 \(i\) 的最大值以及最小的代价。

代码

点击查看代码
int n;
int p[maxn],c[maxn];
int maxid;
//关于p建线段树,则要求下标id时单调增的
struct SegmentTree{
    #define mid ((l+r)>>1)
    #define lson rt<<1,l,mid
    #define rson rt<<1|1,mid+1,r
    int mxid[maxn<<2],mindp[maxn<<2];
    void build(int rt,int l,int r){
        mindp[rt]=0x3f3f3f3f;
        if(l==r) return;
        build(lson),build(rson);
    }
    int calc(int rt,int l,int r,int val){
        if(mxid[rt]<=val) return 0x3f3f3f3f;
        if(l==r) return mindp[rt];
        if(mxid[rt<<1|1]<=val) return calc(lson,val);
        else return min(mindp[rt],calc(rson,val));
    }
    inline void push_up(int rt,int l,int r){
        mxid[rt]=max(mxid[rt<<1],mxid[rt<<1|1]);
        mindp[rt]=calc(lson,mxid[rt<<1|1]);
    }
    void update(int rt,int l,int r,int p,int id,int k){
        if(l==r){
            mxid[rt]=id,mindp[rt]=k;
            return;
        }
        if(p<=mid) update(lson,p,id,k);
        else update(rson,p,id,k);
        push_up(rt,l,r);
    }
    int query(int rt,int l,int r,int p){
        if(r<=p){
            int res=calc(rt,l,r,maxid);
            maxid=max(maxid,mxid[rt]);
            return res;
        }
        if(l==r) return 0;
        int res=0x3f3f3f3f;
        if(p>mid) res=min(res,query(rson,p));
        res=min(res,query(lson,p));
        return res;
    }
    #undef mid
    #undef lson
    #undef rson
}S;
int dp[maxn];
int main(){
    n=read();
    for(int i=1;i<=n;++i) p[i]=read();
    for(int i=1;i<=n;++i) c[i]=read();
    S.build(1,1,n);
    for(int i=1;i<=n;++i){
        maxid=0;
        dp[i]=S.query(1,1,n,p[i]-1);
        if(dp[i]==0x3f3f3f3f) dp[i]=0;
        dp[i]+=c[i];
        S.update(1,1,n,p[i],i,dp[i]);
    }
    maxid=0;
    printf("%d\n",S.query(1,1,n,n));
    return 0;
}

T4 Lost My Music

题意

给定一棵树,点权为 \(w\),对于每个节点 \(u\),其祖先 \(f\),求:

\[\dfrac{w_f-w_u}{dis(u,f)} \]

最小值。

思路

将距离换成深度差,再提出一个负号,得到:

\[-\dfrac{w_f-w_u}{dep_f-dep_u} \]

这是一个斜率,求整体最小,也就是求斜率最大。
可以倍增+二分,将父亲直接定义为可以作为答案的凸包上的父亲。
其实是在模拟一个弹栈的过程。

代码

点击查看代码
int n;
int w[maxn];
vector<int> E[maxn];
int fa[maxn],f[maxn][20];
//交换成乘法算式比较斜率
int dep[maxn];
int check(int x,int y1,int y2){
    return 1ll*(w[x]-w[y1])*(dep[x]-dep[y2])>=1ll*(w[x]-w[y2])*(dep[x]-dep[y1]);
}
void dfs(int u){
    dep[u]=dep[fa[u]]+1;
    int now=fa[u];
    //可以跳到的都是能作为答案的凸包上祖先
    for(int k=18;k>=0;--k){
        if(!f[f[now][k]][0]) continue;
        if(check(u,f[f[now][k]][0],f[now][k])) now=f[now][k];
    }
    if(f[now][0]&&check(u,f[now][0],now)) now=f[now][0];
    f[u][0]=now;
    for(int k=1;k<=18;++k){
        f[u][k]=f[f[u][k-1]][k-1];
    }
    for(int v:E[u]){
        dfs(v);
    }
}
int main(){
    n=read();
    for(int i=1;i<=n;++i) w[i]=read();
    for(int v=2;v<=n;++v){
        fa[v]=read();
        E[fa[v]].push_back(v);
    }
    dfs(1);
    for(int i=2;i<=n;++i){
        db ans=(db)(w[f[i][0]]-w[i])/(dep[i]-dep[f[i][0]]);
        printf("%.10Lf\n",ans);
    }
    return 0;
}

标签:8.4,16,int,res,sum,dep,maxn,now,考后
来源: https://www.cnblogs.com/SoyTony/p/16552382.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有