前言
毒瘤出题人
JZOJ 6290 倾斜的线
题目
分析
考虑按照这些点与斜率 p / q p/q p/q的截距从小到大排序,那么可以证明相邻两个点更接近斜率,然后暴力求解,时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
代码
#include <cstdio>
#include <cctype>
#include <algorithm>
#include <cmath>
#define rr register
using namespace std;
struct rec{int x,y; double b;}a[200001];
int n,p,q,h1,h2,mark; double ans,h;
inline signed iut(){
rr int ans=0,f=1; rr char c=getchar();
while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans*f;
}
bool cmp(rec a,rec b){return a.b<b.b;}
signed main(){
freopen("slope.in","r",stdin);
freopen("slope.out","w",stdout);
n=iut(); p=iut(); q=iut(); h=p*1.0/q; ans=1e18;
for (rr int i=1;i<=n;++i) a[i]=(rec){iut(),iut(),0};
for (rr int i=1;i<=n;++i) a[i].b=a[i].y-h*a[i].x;
sort(a+1,a+1+n,cmp);
for (rr int i=1;i<n;++i){
double t=(a[i+1].y-a[i].y)*1.0/(a[i+1].x-a[i].x);
if (abs(t-h)<ans) ans=abs(t-h),mark=i;
}
rr int h1=a[mark+1].y-a[mark].y,h2=a[mark+1].x-a[mark].x,t=__gcd(h1,h2);
return !printf("%d/%d\n",h1/t,h2/t);
}
JZOJ 6305 最小值
题目
分析
首先朴素的dp方程是
d
p
[
i
]
=
m
i
n
{
d
p
[
j
]
+
F
(
m
i
n
{
a
j
+
1
∼
a
i
}
)
}
dp[i]=min\{dp[j]+F(min\{a_{j+1}\sim a_i\})\}
dp[i]=min{dp[j]+F(min{aj+1∼ai})}
考虑用一个单调递增的栈记录
a
i
a_i
ai那么栈里的可以表示成选择分割的分界点,那么可以
O
(
n
)
O(n)
O(n)解决问题
代码
#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
typedef long long ll;
const int N=200001; int n,a[N],rk[N],top;
ll f[N],w[N],sum[N],dp[N],aa,bb,cc,dd;
inline signed iut(){
rr int ans=0,f=1; rr char c=getchar();
while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans*f;
}
inline ll max(ll a,ll b){return a>b?a:b;}
signed main(){
freopen("min.in","r",stdin);
freopen("min.out","w",stdout);
n=iut(),aa=iut(),bb=iut(),cc=iut(),dd=iut(); sum[0]=-1ll<<60;
for (rr int i=1;i<=n;++i) a[i]=iut();
for (rr int i=1;i<=n;++i) f[i]=aa*a[i]*a[i]*a[i]+bb*a[i]*a[i]+cc*a[i]+dd;
for (rr int i=1;i<=n;++i){
rr ll mx;
for (mx=dp[i-1];top&&a[rk[top]]>a[i];--top) mx=max(mx,w[top]);
rk[++top]=i,w[top]=mx,sum[top]=max(sum[top-1],mx+f[i]);
dp[i]=sum[top];
}
return !printf("%lld\n",dp[n]);
}
JZOJ 6307 安排
题目
给定一个长度为 n ≤ 4096 n≤4096 n≤4096的排列,每次可以交换一个区间的最大值和最小值,求 345678 345678 345678次询问内将一个排列变为另一个排列。
分析
由于操作可逆,问题可以转化为将两个序列分别排序,那么想把一段区间的最小值移到最前端,用快速排序解决,时间复杂度 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)
代码
#include <cstdio>
#include <cctype>
#define rr register
#define swap(x,y) (x^=y,y^=x,x^=y)
using namespace std;
int a[4101],lef[345701],rig[345701],ans,ans1,n;
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline void doit(int l,int r){
for (rr int head=l,tail=r;head<tail;++head,--tail)
swap(a[head],a[tail]),lef[++ans]=head,rig[ans]=tail;
}
inline void mergesort(int l,int mid,int r){
if (l>mid||r<=mid||l==r) return;
rr int i=mid,j=mid+1;
while (l<i&&j<r&&a[i-1]>a[j+1]) --i,++j;
if (a[i]>a[j]){
doit(i,mid),doit(mid+1,j),doit(i,j);
mergesort(l,i-1,mid),mergesort(mid+1,j,r);
}
}
inline void merge(int l,int r){
if (l==r) return;
rr int mid=(l+r)>>1;
merge(l,mid);
merge(mid+1,r);
mergesort(l,mid,r);
}
inline void print(int ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
inline void prnt(int i){putchar(10),print(lef[i]),putchar(32),print(rig[i]);}
signed main(){
freopen("swap.in","r",stdin);
freopen("swap.out","w",stdout);
n=iut();
for (rr int i=1;i<=n;++i) a[i]=iut(); merge(1,n),ans1=ans;
for (rr int i=1;i<=n;++i) a[i]=iut(); merge(1,n),print(ans);
for (rr int i=1;i<=ans1;++i) prnt(i);
for (rr int i=ans;i>ans1;--i) prnt(i);
return 0;
}