11.28日训练赛总结

11.28 日训练赛总结

1 题目

1.1 T1

在[[0, 0], [n, m]] 中有 k 个点, 问从 x=0 移动到 x=n 的过程中,与这些点以及 y=0 和 y=m 的最小距离的最大值为多少?
简单题。将 距离小于等于 2d 的 元素 连起来,若上下边界联通则不合法。考虑最小生成树的过程,做一遍 Ο(n2) 的 Prim 就可以了。

考试时智障,没想到平方 Prim,想用 m log m 的 Prim C 过去,结果单调队列写炸了 其实是线段树 ,最后只有十分……

1.2 T2

给一个序列,选每个数有一个代价,问选出一个 极长(即不可改进)上升 子序列 的最小代价。
据说是套路……
将(i, p[i])转置为(p[i], i),顺序考虑每一个点,记f[i]为 前缀i 以p[i]结尾的极长上升子序列的最小代价。
若f[j]能转移到f[i],则(p[j], j)一定在顺x轴生成的递减单调栈上。
因为将两个单调栈“相接”,后者对前者的影响归结于其首个元素(栈底)元素,所以不妨记mxf(l, r, x)为区间[l, r]内的单调栈在插入x后所剩余点的最大f[]。
那么把问题放到线段树上做,同时每个左儿子记录一下mxf(l, mid, [mid+1, r]的单调栈顶)即可。

1.3 T3

对每一个点 u 求

\begin{equation}\min\{\frac{c_v-c_u}{\text{dis}(u, v)}|v 是 u 的祖先\}\end{equation}

链上显然就是一个求凸包的过程,那么到树上呢?
注意到在树上即使支持暴力弹栈也没有复杂度保证,所以需要数据结构支持。
什么数据结构?Splay?主席树?
只需要每个点记录插入该点时的倍增数组就行了。

2 代码

2.1 T3
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;

#define Re return
#define In inline
#define Rg register
#define St static
#define inc(l, i, r) for(Rg ll i=l; i<r; ++i)
#define dec(l, i, r) for(Rg ll i=r; i>l; --i)
typedef long long ll;

const ll mxn = 1<<19;

ll h[mxn], e[mxn], d[mxn], c[mxn], anc[mxn][20];

In bool chk(ll u, ll v, ll w)
{Re (d[w]-d[u])*(c[v]-c[u])>=(c[w]-c[u])*(d[v]-d[u]);}

void DFS(ll u=1, ll f=0)
{
    anc[u][0]=f;
    inc(1, i, 20)
        anc[u][i]=anc[u][i-1][anc][i-1];
//    if(u==23)
//        inc(0, i, 20)
//            printf("%lld\n", anc[u][i]);
    d[u]=d[f]+1;
/*
    v 的目标是 min 将要弹出栈中的点
    (minimize (c_w-c_u)/(d_u-d_w) 即 最大化斜率)
*/
    Rg ll v=u;
    dec(-1, i, 19)
    {
        Rg int w = anc[v][i];
        if(w>1 && chk(anc[w][0], w, u)) v=w;
//        if(u==23) printf("%lld: %lld\n", u, v);
    }
    inc(0, i, 20) anc[u][i] = anc[v][i];

//    printf("%lld: ", u);
//    inc(0, i, 5) printf("%lld ", anc[u][i]);
//    puts("");
    
    for(v=u[h]; v; v=v[e])
        DFS(v, u);
}

int main()
{
    St ll n;
    scanf("%lld", &n);
    inc(1, i, n+1) scanf("%lld", c+i);
    inc(2, v, n+1)
    {
        St ll u;
        scanf("%lld", &u),
        v[e]=u[h], u[h]=v;
    }
    DFS();
//    puts("fuck");
    inc(2, i, n+1)
        printf("%.10lf ", (double)(c[anc[i][0]]-c[i])/(d[i]-d[anc[i][0]]));
    puts("");
/*    inc(2, i, n+1)
        printf("%lld ", anc[i][0]);
    puts("");*/
//    printf("%lld\n", anc[23][0]);
    Re 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值