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 求
链上显然就是一个求凸包的过程,那么到树上呢?
注意到在树上即使支持暴力弹栈也没有复杂度保证,所以需要数据结构支持。
什么数据结构?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;
}
#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;
}