【模拟赛】纪中提高A组 19.8.17 测试

Task.1 倾斜的线

题目大意:在平面上有 \(n\) 个点,给定 \(P,Q\),在平面上找到两个点使它们所在直线的斜率数值上最接近 \(\frac{P}{Q}\) 。对于接近程度相同的直线选择较小的斜率。

数据范围:\(2\leq N\leq 2\times 10^5,1\leq P,Q,x,y\leq 10^9\),不存在经过两点的直线斜率为 \(0\) 或正无穷。

在考场想到了一个没那么naive的想法,把坐标系旋转到 \(y\) 轴平行于斜率为 \(\frac{P}{Q}\) 的直线的位置,大概是这样:

算出来每个点在新坐标系中的横坐标是 \(\frac{Px-Qy}{\sqrt{P^2+Q^2}}\),下面非常的恼人,但是和 \(x,y\) 无关,所以让它消失,变成 \(Px-Qy\)。

发现经过横坐标相近的点的直线斜率很接近 \(\frac{P}{Q}\),所以我们按照横坐标排序后去算相邻点在原图中的斜率。

但是这个方法挂了,应该是我写得有问题,其实这个方法已经非常接近正解了,思路上没有什么问题。

我们再把做法变得具体一点:在新坐标系中放许多 \(x=c\) 平行 \(y\) 轴的直线,探讨最优解会在什么地方。

如图:

很明显的,对于按 \(x\) 排序后相近的三个点 \(A,B,C\),最优解一定是在 \(AB\) 或 \(BC\) 中取,而不是 \(AC\)(\(B\) 点不管放在过 \(AC\) 直线间的什么位置 \(AC\) 都没有 \(AB,BC\) 优)。

而 \(std\) 给出的做法是每个点以过它本身的斜率为 \(\frac{P}{Q}\) 的直线的截距排序,然后从排序后相邻的点对中计算最优解,正确性的证明与上述过程基本一致。复杂度 \(O(N log N)\)。

实现上的坑点:\(std\) 建议我们写分数类来避免精度误差,但是几乎所有人都是 \(long\ double\) 过去的...

代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

template<class T>void read(T &x){
    x=0; char c=getchar();
    while(c<'0'||'9'<c)c=getchar();
    while('0'<=c&&c<='9'){x=(x<<1)+(x<<3)+(c^48); c=getchar();}
}
typedef long long ll;
typedef long double db;
const int N=200050;
const db e=1e-15;
int n;
ll P,Q,p,q;
struct point{ll x,y,c;}a[N];
ll gcd(ll _a,ll _b){return _b==0?_a:gcd(_b,_a%_b);}
ll absl(ll _a){return _a>0?_a:-_a;}
db absd(db _a){return _a>0?_a:-_a;}
bool cmp(point x,point y){return x.c<y.c;}

int main(){
    freopen("slope.in","r",stdin);
    freopen("slope.out","w",stdout);
    read(n); read(P); read(Q);
    ll d=gcd(P,Q); P/=d; Q/=d;
    for(int i=1,x,y;i<=n;i++){
        read(x); read(y);
        a[i]=(point){x,y,1ll*Q*y-1ll*P*x};
    }
    sort(a+1,a+n+1,cmp);
    ll tp,tq,p,q;
    db r=1e10,t,s=(db)P/(db)Q;
    for(int i=1;i<n;i++){
        tp=a[i].y-a[i+1].y;
        tq=a[i].x-a[i+1].x;
        if(tp*tq<0) continue;
        tp=absl(tp); tq=absl(tq);
        d=gcd(tp,tq); tp/=d; tq/=d;
        t=absd((db)tp/(db)tq-s);
        if(r-t>e) r=t, p=tp, q=tq;
    }
    printf("%lld/%lld\n",p,q);
    return 0;
}

Task.2 最小值

题目大意:有一个长度为 \(n\) 的序列 \(a\),定义一段区间 \([l,r]\) 的价值为 \(f(min^r_{i=l}a_i)=Ax^3+Bx^2+Cx+D\)。现在需要你把区间分成若干段,求分割后所有区间的价值的最大和。

数据范围:\(1\leq N\leq 2\times 10^5,\forall |f(x)|\leq 10^{13}\),输入的数据均在 \(int\) 范围内。

咋一看式子和求的东西以为是斜率优化?毕竟那个 \(O(N^2)\) 的\(dp\)还是很明显的:\(dp_i=max\{dp_j+f(min^r_{i=l}a_i)\}\)。接下来就是考虑优化它了,变成带 \(log\) 或者线性都会很好。

但是最小值有些棘手,我甚至不知道该怎么描述。但是在转移的过程中,随着 \(i\) 的增加, \(i\) 之前某些区间 \([l,r]\) 内的位置到 \(i\) 的最小值都是相同的,这个时候区间内的最优解一定是 \(dp\) 值最大的那一个。利用这个性质,如果我们能知道哪些区间里的位置到 \(i\) 的最小值相同,我们就能避免一些无用的转移。

这个问题就转化成了求 \(i\) 之前离 \(i\) 最近的位置 \(x\) ,使 \(x\) 之后的位置到 \(i\) 的最小值都是 \(a_x\),再求 \(x\) 之前离 \(x\) 最近的位置 \(y\),使 \(y\) 到 \(x-1\) 的位置到 \(i\) 的最小值都为 \(a_y\) ...... 这个问题就一个经典的单调栈问题,维护一个栈顶到栈底递减的单调栈就可以了。

再利用单调栈来优化转移:当 \(a_i\) 入栈前退栈时,把退栈的位置的最值记录下来,利用退栈的最值更新 \(dp_i\),再把最值也存下来(可以存在栈里)。复杂度 \(O(N)\)。

代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

template<class T>void read(T &x){
    x=0; bool f=0; char c=getchar();
    while(c<'0'||'9'<c){f|=(c=='-'); c=getchar();}
    while('0'<=c&&c<='9'){x=(x<<1)+(x<<3)+(c^48); c=getchar();}
    x=f?-x:x;
}
typedef long long ll;
const int N=200050;
int n,A,B,C,D;
int a[N],top;
struct node{int i; ll v;}st[N];
ll f[N];

ll F(int x){return 1ll*A*x*x*x+1ll*B*x*x+1ll*C*x+D;}

int main(){
    freopen("min.in","r",stdin);
    freopen("min.out","w",stdout);
    read(n); read(A); read(B); read(C); read(D);
    for(int i=1;i<=n;i++) read(a[i]);
    ll tmp=0;
    for(int i=1;i<=n;i++){
        tmp=f[i-1];
        while(top&&a[st[top].i]>a[i]) tmp=max(tmp,st[top--].v);
        f[i]=tmp+F(a[i]);
        if(top) f[i]=max(f[i],f[st[top].i]);
        st[++top]=(node){i,tmp};
    }
    printf("%lld\n",f[n]);
    return 0;
}

Task.3 安排

题目大意:给出长度为 \(n\) 的排列 \(A,B\),每次操作可以选择 \(A\) 中的一段区间 \([l,r]\),交换区间内最大值最小值的位置。求一种在 \(345678\) 次内把 \(A\) 变成 \(B\) 的方法。

数据范围:\(1\leq N\leq 4096\)

目前还不会做...

“您能再~讲一遍吗?”

原文地址:https://www.cnblogs.com/opethrax/p/11369880.html

时间: 2024-08-08 13:53:21

【模拟赛】纪中提高A组 19.8.17 测试的相关文章

【模拟赛】纪中提高A组 19.8.9 测试

Task.1 走格子 题目大意:C和F在一个可以看作 \(N\times M\) 的矩阵的房间中,矩阵中的每一个元素可以是: 障碍:"#" C或者F的起点:"C"或"F" 空区域:"." C携带了一把传送枪,每次C都可以: 花费一个单位时间移动到相邻的空区域 不花费时间向上下左右之一的方向的墙壁上发射传送门(传送门最多只能同时存在两扇,如果已经存在两扇再发射一扇那最早出现的那扇会消失,一个位置不能存在两扇传送门) 花费一个单位

纪中某日c组模拟赛 2314. 最短路

2314. 最短路 (File IO): input:dti.in output:dti.out 时间限制: 1000 ms  空间限制: 262144 KB  具体限制 Goto ProblemSet 题目描述 输入 样例输出 数据范围限制 原文地址:https://www.cnblogs.com/send-off-a-friend/p/11359152.html

【题解】JZOJ 提高A组 19.8.10 挖宝藏

二进制枚举子集 先给出代码: for(int o = s; o; o = (o - 1) & s) 其中\(s\)为当前的状态,\(o\)为枚举的子集.根据与运算的性质我们得到的显然是s的子集,但是为什么这样做可以得到\(s\)所有的子集? 网上的一种说法是把状态\(s\)看做忽略\(0\)的二进制数,只考虑每次对这个二进制数减一,过程大概是: 假如 \(s=(0101101)_2\quad s_0=(1111)_2\) \(s_1=(s_0-1)\&s=(1110)_2\) \(s_2=

【NOIP 模拟赛】中值滤波 打表找规律

对于这样看起来不像什么算法也没什么知识点的题,一脸懵逼的话不是手推规律就是打表找规律......... 当然还有一些超出你能力之外的数学题...... #include <cstdio> const int N=500010; int n,ans,A[N]; inline int Max(int x,int y){ return x>y?x:y; } int main(){ scanf("%d",&n); int last,now,P=0; for(int i

【前行】◇第3站◇ 国庆训练营&#183;OI制模拟赛

[第3站] 国庆训练营·OI制模拟赛Ⅰ 怀着冲刺提高组400的愿望来到这个very small but very interesting 的训练营QwQ 在北大dalao的带领下开始了第一场OI模拟赛[炸心态ヽ(*.>Д<)o゜] ? 简单总结 感觉非常爆炸…… 第一题还好,一眼看出结论题,所以开始打表……没想到只打出来了一种情况(为什么全是特殊情况),然后就凉了. 第二题就开始崩溃了.首先画图思考了大概20分钟……然后发现想不出正解,就开始想要骗分.看了看数据阶梯,发现自己好像只能做前1/3

2017.11.25【NOIP提高组】模拟赛A组

2017.11.25[NOIP提高组]模拟赛A组 T1 3467. [NOIP2013模拟联考7]最长上升子序列(lis) T2 3468. [NOIP2013模拟联考7]OSU!(osu) T3 3472. [NOIP2013模拟联考8]匹配(match) T1 有转移方程f[i]=max{f[j]}+1,a[j]<a[i] 可以用线段树+离散化维护这个方程,因为涉及以往状态可以用主席树维护 打太丑爆空间了 Code 1 #include<cstdio> 2 #include<c

2017.12.02【NOIP提高组】模拟赛A组

2017.12.02[NOIP提高组]模拟赛A组 T1 3555[GDKOI2014模拟]树的直径 T2 3542[清华集训2014]冒泡排序 T3 3486[NOIP2013模拟联考10]道路改建(rebuild) T1 树直径的一个性质,两棵树合并,形成新的树的直径的两个端点为原树中的四个端点之二. 可以用反证法证明.用此性质本题就变成了lca裸题了 Code #include<cstdio> #include<cstring> #include<cmath> #i

2017.12.09【NOIP提高组】模拟赛A组

2017.12.09[NOIP提高组]模拟赛A组 T1 3489. [NOIP2013模拟联考11]数列的GCD(gcd) T2 3500.[NOIP2013模拟联考15]物语(monogatari) T3 3501.[NOIP2013模拟联考15]消息传递(news) 吐槽:这次的题好像有点水啊,但最简单的第二题都给打挂啦!!(数组开小了) T1 本套题中最难的题.考虑dp 设f[i]是b[1],b[2]...b[N]的最大公约数的数目,g[i]是b[1],b[2]...b[N]的公约数的数目

ZROI提高组模拟赛05总结

ZROI提高组模拟赛05总结 感觉是目前为止最简单的模拟赛了吧 但是依旧不尽人意... T1 有一半的人在30min前就A掉了 而我花了1h11min 就是一个简单的背包,我硬是转化了模型想了好久,生生把一个弱智题变成了一个不可做题 最后竟然在转化两次后的模型上以为自己做出来了 这个题比别人多花的1h左右的时间,而且只得到了30分,成为了这场比赛失败的关键因素 T2 依旧是一道简单题 有人20min之内就A掉了 感觉放在CF里最多算一道Div2 D,还是简单的那种 可是我又一次想复杂了 大意就是